файл синтаксического анализа для сбора заголовков разделов с последующим содержимым

#python #debugging #python-2.x #text-processing

#python #отладка #python-2.x #обработка текста

Вопрос:

Мне нужно объединить строки (заглавными буквами) моего входного файла в одну строку, как показано ниже:

file1-inp

 =4455
AAAAAAAAAA
BBBBBBBBBBB
CCCCCCCCCC
=3433
GGGGGGGGGGGG
DDDDDDDDDDD
EEEEEEEEEEE
=44543
FFFFFFFFFFFFF
HHHHHHHHHHHHH
 

ожидаемый результат

 =4455
AAAAAAAAAABBBBBBBBBBB
CCCCCCCCCC
=3433
GGGGGGGGGGGGDDDDDDDDDDDEEEEEEEEEEE
=44543
FFFFFFFFFFFFFHHHHHHHHHHHHH
 

мой код

 fp=open("file1","r")
a=[]
for line in fp:
    if line[0]=="=":
        print line.strip()
        print "".join(a)
        a=[]
    else:
        a.append(line.strip())
 

Фактический результат

 =4455

=3433
AAAAAAAAAABBBBBBBBBBB
CCCCCCCCCC
=44543
GGGGGGGGGGGGDDDDDDDDDDDEEEEEEEEEEE
 

Я знаю, что это довольно глупо, но может кто-нибудь, пожалуйста, помочь мне исправить проблему в моем коде?

Комментарии:

1. Это весь код целиком? Я получаю NameError: name 'a' is not defined .

2. Проблема в том, что если a значение пусто, print "".join(a) то все равно будет выводиться новая строка.

3. Другая проблема заключается в том, что print "".join(a) будет напечатано содержимое a , которое в данный момент содержит все строки из предыдущего раздела файла, а не раздел, заголовок которого вы только что обнаружили.

4. @Kevin если я удалю эту строку «a=[]», то a продолжит добавлять все строки прописными буквами =4455 =3433 AAAAAAAAAABBBBBBBBBBB CCCCCCCCCC =44543 AAAAAAAAAABBBBBBBBBBB CCCCCCCCCCGGGGGGGGGGGGDDDDDDDDDDDEEEEEEEEEEE

5. @diablo8226: я бы предложил печатать только a в том случае, если он не пустой.

Ответ №1:

Альтернативный подход, который может быть проще читать и поддерживать, если логика становится более сложной — создайте dict во время цикла for, а затем распечатайте (или любую другую логику) впоследствии:

 fp=open("file1","r")
mydict = {}

for line in fp:
    if line[0]=="=":
        key = line.strip()
    else:
        mydict.setdefault(key,[]).append(line.strip())

for key, value in mydict.iteritems():
    print key
    print "".join(value)
 

Стоит отметить: этот подход будет (может) влиять на порядок разделов во время вывода, поскольку стандартный Python dict не гарантирует порядок ключей. Если вы используете Python 2.7 или выше, вы можете вместо этого использовать OrderedDict , который сохраняет порядок, в котором ключи были впервые вставлены, и является подклассом dict, поэтому его можно легко поменять местами.

Комментарии:

1. Возможно, стоит отметить, что словарный подход изменит порядок вывода (и почему), теперь, когда я думаю об этом…

2. OrderedDict возможно?

3. Точно. Спасибо за улов. 🙂

Ответ №2:

Вместо того чтобы печатать внутри цикла, я бы просто накопил все, что нужно распечатать, и вывел бы это в конце. Если вы видите строку заголовка, добавьте ее и начните накапливать строки. Когда вы увидите следующий заголовок, добавьте соединенные строки и следующий заголовок и т. Д.

 with open('file1') as f:
    lines = f.read().splitlines()

out = []  # will accumulate lines to be output
items = []  # will accumulate lines between headers

for line in lines:
    line = line.strip()

    if not line:  # ignore blank lines
        continue

    if line.startswith('='): # new header, join the accumulated items
        if items:  # don't add a blank line if no lines were accumulated
            out.append(''.join(items))

        out.append(line)  # accumulate new header
        items = []

        continue

    items.append(line)  # accumulate non-header lines

if items:  # handle last accumulated items
    out.append(''.join(items))

print 'n'.join(out)  # out is now a list of header, joined lines, header...
 

Ответ №3:

Ваша проблема в том, что вы печатаете "".join(a) после line.strip() , а не до. Исправленная версия:

 a = []
fp=open("file1","r")
for line in fp:
    if line[0]=="=":
        if a:  #  prevent printing a blank line at the start
            print "".join(a)
        print line.strip()
        a=[]
    else:
        a.append(line.strip())
print "".join(a)
 

a инициализацией перед циклом и окончательным содержимым a , напечатанным впоследствии).

Комментарии:

1. чтобы избежать пустой строки, используйте : if a: print "".join(a)

2. @sfk да, я просто подумал об этом сам 🙂 Добавлено исправление.

3. Вы также можете сделать его немного более элегантным, создав dict во время цикла for, а затем напечатав в конце. Возможно, установить key = line.strip() , когда line[0]=="=" , иначе mydict.getdefault(key,[]).append(line.strip()) … основным преимуществом будет удобочитаемость и стабильность, если вам нужно усложнить логику.

4. @DreadPirateShawn согласен — этот ответ направлен на исправление ошибки в существующем коде, но другой ответ в соответствии с вашими предложениями был бы полезен. Если вы напишете это, я поддержу это…

5. @ZeroPiraeus Справедливо, я писал то же самое, что и вы. 🙂 Просто подумал, что стоит обратить внимание на неловкость, подразумеваемую в потоке в стиле «обратной ссылки» при печати предыдущего раздела каждый раз, когда запускается новый раздел, а затем печать последнего раздела после завершения цикла.

Ответ №4:

TXR

 @(repeat)
=@blah
@  (collect)
@lines
@  (until)
=@/.*/
@  (end)
@  (cat lines "")
@  (output)
=@blah
@lines
@  (end)
@(end)
 

Выполнить:

 $ txr data.txr data
=4455
AAAAAAAAAABBBBBBBBBBBCCCCCCCCCC
=3433
GGGGGGGGGGGGDDDDDDDDDDDEEEEEEEEEEE
=44543
FFFFFFFFFFFFFHHHHHHHHHHHHH
 

TXR Lisp:

 $ txr -t '[mapcar cat-str (partition-by (opip first (= #=)) (get-lines))]' < data
=4455
AAAAAAAAAABBBBBBBBBBBCCCCCCCCCC
=3433
GGGGGGGGGGGGDDDDDDDDDDDEEEEEEEEEEE
=44543
FFFFFFFFFFFFFHHHHHHHHHHHHH
 

Awk:

 /=.*/   { printf("%s", out);
          blah = $0; line = ""; next }
        { line = line $0
          out = blah "n" line "n" }
END     { printf("%s", out); }
 

Выполнить:

 $ awk -f data.awk data
=4455
AAAAAAAAAABBBBBBBBBBBCCCCCCCCCC
=3433
GGGGGGGGGGGGDDDDDDDDDDDEEEEEEEEEEE
=44543
FFFFFFFFFFFFFHHHHHHHHHHHHH