Регулярное выражение, соответствующее многострочному блоку текста
у меня возникли проблемы с получением регулярного выражения Python для работы при сопоставлении с текстом, который охватывает несколько строк. Пример текста ('n ' - это новая строка)
some Varying TEXTn
n
DSJFKDAFJKDAFJDSAKFJADSFLKDLAFKDSAFn
[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"(^[^>][ws]+)$", re.MULTILINE|re.DOTALL) # just textlines
и много вариаций здесь не повезло. Этот последний, кажется, соответствует строкам текста один за другим, что не то, что я действительно хочу. Я могу поймать первую часть, без проблем, но я не могу поймать 4-5 строк прописного текста.
Я бы хотел совпадение.группы(1), чтобы быть some_Varying_Text и группы(2) быть строка1+строка2+строка3+и т. д. до тех пор, пока пустая строка встречается.
6 ответов:
попробуйте это:
re.compile(r"^(.+)\n((?:\n.+)+)", re.MULTILINE)Я думаю, что ваша самая большая проблема заключается в том, что вы ожидаете
^и$якоря, чтобы соответствовать строк, но они не. В многострочном режиме,^соответствует позиции немедленно после новая строка и$соответствует позиции немедленно предыдущий символ.будьте в курсе также, что новая строка может содержать символы перевода строки (\n) и возврат каретки (\R), или возврат каретки+перевод строки (\р\н). Если вы не уверены, что ваш целевой текст использует только перевод строк, вы должны использовать эту более инклюзивную версию регулярного выражения:
re.compile(r"^(.+)(?:\n|\r\n?)((?:(?:\n|\r\n?).+)+)", re.MULTILINE)кстати, вы не хотите использовать модификатор DOTALL здесь; вы полагаетесь на то, что точка соответствует всему за исключением переводы строк.
это будет работать:
>>> 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)+)означает совпадение одного или нескольких текстовые строки но не помещайте каждую строку в группу. Вместо этого поставьте все the текстовые строки в одном группа.- вы можете добавить окончательный
\nв регулярном выражении, если вы хотите применить двойную новую строку в конце.- кроме того, если вы не уверены в том, какой тип новой строки вы получите (
\nили\rили\r\n) затем просто исправьте регулярное выражение, заменив каждое вхождение\nby(?:\n|\r\n?).
Если каждый файл имеет только одну последовательность аминокислот, я бы вообще не использовал регулярные выражения. Просто что-то вроде этого:
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
найти:
^>([^\n\r]+)[\n\r]([A-Z\n\r]+)\1 = some_varying_text
\2 = строки всех заглавных букв
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])
мои предпочтения.
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 )сделать одну строку.Я нахожу это менее разочаровывающим (и более гибким), чем многострочные регулярные выражения.
ниже приведено регулярное выражение, соответствующее многострочному блоку текста:
import re result = re.findall('(startText)(.+)((?:\n.+)+)(endText)',input)
Comments