2009-02-25 4 views
60

У меня возникли проблемы с получением регулярного выражения Python при сопоставлении с текстом, который охватывает несколько строк. Пример текста («\ п» перевод строки)Регулярное выражение, соответствующее многострочному блоку текста

some Varying TEXT\n 
\n 
DSJFKDAFJKDAFJDSAKFJADSFLKDLAFKDSAF\n 
[more of the above, ending with a newline]\n 
[yep, there is a variable number of lines here]\n 
\n 
(repeat the above a few hundred times). 

Я хотел бы, чтобы захватить две вещи: часть «some_Varying_TEXT», и все строки текста в верхнем регистре, который поставляется две строки ниже в один захват (я могу вычеркнуть символы новой строки позже). Я попытался с помощью нескольких подходов:

re.compile(r"^>(\w+)$$([.$]+)^$", re.MULTILINE) # try to capture both parts 
re.compile(r"(^[^>][\w\s]+)$", re.MULTILINE|re.DOTALL) # just textlines 

и много вариаций настоящего, не повезло. Последний, похоже, соответствует строкам текста один за другим, чего я не хочу. Я могу поймать первую часть, без проблем, но, похоже, я не могу поймать 4-5 строк текста в верхнем регистре. Я хотел бы, чтобы match.group (1) был некотором _. Варьирование _ Текст и группа (2) должны быть линией1 + строка2 + строка3 + и т. Д. До тех пор, пока не встретится пустая строка.

Если кто-то любопытен, его предполагается представлять собой последовательность аминокислот, составляющих белок.

+0

Есть ли что-то еще в файле, кроме первой строки и верхнего регистра? Я не уверен, почему вы будете использовать регулярное выражение, а не разделять текст на символы новой строки и считать первый элемент «some_Varying_TEXT». – UncleZeiv

+2

yes, regex - неправильный инструмент для этого. – hop

+0

Ваш образец текста не имеет символа ведущего '>'. Должно ли это? – MiniQuark

ответ

81

Попробуйте это:

re.compile(r"^(.+)\n((?:\n.+)+)", re.MULTILINE) 

Я думаю, что самой большой проблемой является то, что вы ожидаете в ^ и $ якоря, чтобы соответствовать символы новой строки, но они этого не делают. В многолинейном режиме ^ соответствует положению немедленно после a newline и $ соответствует непосредственно позиции a newline.

Помните, что новая строка может состоять из перевода строки (\ n), возврата каретки (\ r) или возврата каретки + перевода строки (\ r \ n). Если вы не уверены, что ваш целевой текст использует только символы перевода строки, вы должны использовать эту инклюзивную версию регулярных выражений:

re.compile(r"^(.+)(?:\n|\r\n?)((?:(?:\n|\r\n?).+)+)", re.MULTILINE) 

Кстати, вы не хотите использовать модификатор DOTALL здесь; вы полагаетесь на то, что точка соответствует всем кроме.

+0

Вы можете захотеть заменить вторую точку в регулярном выражении на [A-Z], если вы не хотите, чтобы это регулярное выражение соответствовало любому текстовому файлу с пустой второй строкой. ;-) – MiniQuark

+0

Мое впечатление, что файлы-мишени будут соответствовать определенному (и повторяющемуся) шаблону пустых или непустых строк, поэтому не обязательно указывать [AZ], но это, вероятно, не повредит , или. –

+0

Это решение прекрасно работало. В отпуске я извиняюсь, так как я, очевидно, недостаточно прояснил ситуацию (а также за опоздание этого ответа). Спасибо за вашу помощь! – Jan

1

находка:

^>([^\n\r]+)[\n\r]([A-Z\n\r]+) 

\ 1 = some_varying_text

\ 2 = линии всех CAPS

Edit (доказательство того, что это работает):

text = """> some_Varying_TEXT 

DSJFKDAFJKDAFJDSAKFJADSFLKDLAFKDSAF 
GATACAACATAGGATACA 
GGGGGAAAAAAAATTTTTTTTT 
CCCCAAAA 

> some_Varying_TEXT2 

DJASDFHKJFHKSDHF 
HHASGDFTERYTERE 
GAGAGAGAGAG 
PPPPPAAAAAAAAAAAAAAAP 
""" 

import re 

regex = re.compile(r'^>([^\n\r]+)[\n\r]([A-Z\n\r]+)', re.MULTILINE) 
matches = [m.groups() for m in regex.finditer(text)] 

for m in matches: 
    print 'Name: %s\nSequence:%s' % (m[0], m[1]) 
+0

Похоже на меня. Вы проверили это? – Triptych

+0

Он делает, я добавил для вас некоторый код. –

+0

К сожалению, это регулярное выражение также будет соответствовать группам заглавных букв, разделенных пустыми строками. Однако это может быть не очень. – MiniQuark

14

Это будет работать:

>>> import re 
>>> rx_sequence=re.compile(r"^(.+?)\n\n((?:[A-Z]+\n)+)",re.MULTILINE) 
>>> rx_blanks=re.compile(r"\W+") # to remove blanks and newlines 
>>> text="""Some varying text1 
... 
... AAABBBBBBCCCCCCDDDDDDD 
... EEEEEEEFFFFFFFFGGGGGGG 
... HHHHHHIIIIIJJJJJJJKKKK 
... 
... Some varying text 2 
... 
... LLLLLMMMMMMNNNNNNNOOOO 
... PPPPPPPQQQQQQRRRRRRSSS 
... TTTTTUUUUUVVVVVVWWWWWW 
... """ 
>>> for match in rx_sequence.finditer(text): 
... title, sequence = match.groups() 
... title = title.strip() 
... sequence = rx_blanks.sub("",sequence) 
... print "Title:",title 
... print "Sequence:",sequence 
... print 
... 
Title: Some varying text1 
Sequence: AAABBBBBBCCCCCCDDDDDDDEEEEEEEFFFFFFFFGGGGGGGHHHHHHIIIIIJJJJJJJKKKK 

Title: Some varying text 2 
Sequence: LLLLLMMMMMMNNNNNNNOOOOPPPPPPPQQQQQQRRRRRRSSSTTTTTUUUUUVVVVVVWWWWWW 

Некоторые объяснения об этом регулярное выражение может быть полезно: ^(.+?)\n\n((?:[A-Z]+\n)+)

  • Первый символ (^) означает "начиная с начала строки". Имейте в виду, что он не соответствует самой новой строке (то же самое для $: это означает «прямо перед новой строкой», но она не соответствует самой новой строке).
  • Тогда (.+?)\n\n означает «соответствовать как можно меньшему количеству символов (все символы разрешены), пока вы не достигнете двух новых строк». Результат (без новых строк) помещается в первую группу.
  • [A-Z]+\n означает «матч, как многие прописные буквы, как это возможно, пока вы не достигнете новой строки. Это определяет то, что я буду называть TextLine.
  • ((?:TextLine)+) означает соответствие один или несколько объектов TextLine но не положить каждую строку в группе. Вместо этого поместите всеобъектов TextLine в одной группе.
  • вы можете добавить окончательный \n в регулярном выражении, если вы хотите обеспечить двойной перевод строки в конце.
  • Кроме того, если вы не уверены в том, какой тип новой строки вы получите (\n или \r или \r\n), то просто исправить регулярное выражение, заменяя каждое вхождение \n по (?:\n|\r\n?).
+0

match() возвращает только одно совпадение в самом начале целевого текста, но ОП говорит, что будет сотен матчей на файл. Я думаю, вы бы хотели finditer(). –

+1

@ Алан: Просто исправлено, спасибо. – MiniQuark

1

Мои предпочтения.

lineIter= iter(aFile) 
for line in lineIter: 
    if line.startswith(">"): 
     someVaryingText= line 
     break 
assert len(lineIter.next().strip()) == 0 
acids= [] 
for line in lineIter: 
    if len(line.strip()) == 0: 
     break 
    acids.append(line) 

На данный момент вы someVaryingText в виде строки, и кислот в виде списка строк. Вы можете сделать "".join(acids), чтобы сделать одну строку.

Я нахожу это менее расстраивающим (и более гибким), чем многострочные регулярные выражения.

4

Если у каждого файла есть только одна последовательность аминокислот, я бы вообще не использовал регулярные выражения. Что-то вроде этого:

def read_amino_acid_sequence(path): 
    with open(path) as sequence_file: 
     title = sequence_file.readline() # read 1st line 
     aminoacid_sequence = sequence_file.read() # read the rest 

    # some cleanup, if necessary 
    title = title.strip() # remove trailing white spaces and newline 
    aminoacid_sequence = aminoacid_sequence.replace(" ","").replace("\n","") 
    return title, aminoacid_sequence 
+0

Определенно самый простой способ, если был только один, и его также можно работать с большим количеством, если добавлена ​​еще одна логика. Однако в этом конкретном наборе данных имеется около 885 белков, и я чувствовал, что регулярное выражение должно быть в состоянии справиться с этим. – Jan

Смежные вопросы