Biểu thức chính quy khớp với một khối văn bản nhiều dòng


105

Tôi đang gặp một chút khó khăn khi bắt một regex Python hoạt động khi khớp với văn bản kéo dài nhiều dòng. Văn bản ví dụ là ('\ n' là một dòng mới)

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).

Tôi muốn chụp hai thứ: phần 'some_Varying_TEXT' và tất cả các dòng văn bản viết hoa đi kèm hai dòng bên dưới nó trong một lần chụp (tôi có thể loại bỏ các ký tự dòng mới sau này). Tôi đã thử với một số cách tiếp cận:

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

và rất nhiều biến thể của nó mà không có may mắn. Cái cuối cùng có vẻ khớp với từng dòng văn bản, đó không phải là điều tôi thực sự muốn. Tôi có thể nắm bắt được phần đầu tiên, không vấn đề gì, nhưng tôi dường như không thể nắm bắt được 4-5 dòng văn bản viết hoa. Tôi muốn match.group (1) là some_Varying_Text và group (2) là line1 + line2 + line3 + v.v. cho đến khi gặp dòng trống.

Nếu ai đó tò mò, nó được cho là một chuỗi các aminoacid tạo nên một loại protein.


Có thứ gì khác trong tệp ngoài dòng đầu tiên và văn bản viết hoa không? Tôi không chắc tại sao bạn lại sử dụng regex thay vì tách tất cả văn bản thành các ký tự dòng mới và lấy phần tử đầu tiên là "some_Varying_TEXT".
UncleZeiv 25/02/09

2
vâng, regex là công cụ sai cho việc này.

Văn bản mẫu của bạn không có >ký tự đứng đầu . Có nên không?
MiniQuark

Câu trả lời:


114

Thử cái này:

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

Tôi nghĩ rằng vấn đề lớn nhất của bạn là bạn đang mong đợi ^$neo khớp với các nguồn cấp dữ liệu, nhưng chúng không. Ở chế độ nhiều dòng, ^khớp với vị trí ngay sau dòng mới và $khớp với vị trí ngay trước dòng mới.

Cũng cần lưu ý rằng một dòng mới có thể bao gồm dòng cấp dữ liệu (\ n), ký tự xuống dòng (\ r) hoặc ký tự xuống dòng + dòng cấp dữ liệu (\ r \ n). Nếu bạn không chắc rằng văn bản mục tiêu của mình chỉ sử dụng dòng cấp dữ liệu, bạn nên sử dụng phiên bản regex bao gồm hơn này:

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

BTW, bạn không muốn sử dụng công cụ sửa đổi DOTALL ở đây; bạn đang dựa vào thực tế là dấu chấm khớp với mọi thứ ngoại trừ các dòng mới.


Bạn có thể muốn thay thế dấu chấm thứ hai trong regex bằng [AZ] nếu bạn không muốn cụm từ thông dụng này chỉ khớp với bất kỳ tệp văn bản nào có dòng thứ hai trống. ;-)
MiniQuark 25/02/09

Ấn tượng của tôi là các tệp đích sẽ tuân theo một mẫu xác định (và lặp lại) gồm các dòng trống so với không trống, vì vậy không cần thiết phải chỉ định [AZ], nhưng nó có thể sẽ không ảnh hưởng gì.
Alan Moore

Giải pháp này hoạt động tuyệt vời. Ngoài ra, tôi xin lỗi, vì rõ ràng là tôi đã không làm rõ tình hình đủ (và cả vì sự chậm trễ của câu trả lời này). Cảm ơn bạn đã giúp đỡ!
Ngày

21

Điều này sẽ hoạt động:

>>> 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

Một số giải thích về cụm từ thông dụng này có thể hữu ích: ^(.+?)\n\n((?:[A-Z]+\n)+)

  • Ký tự đầu tiên ( ^) có nghĩa là "bắt đầu ở đầu dòng". Lưu ý rằng nó không khớp với chính dòng mới (tương tự với $: nó có nghĩa là "ngay trước một dòng mới", nhưng nó không khớp với chính dòng mới).
  • Sau đó (.+?)\n\ncó nghĩa là "khớp càng ít ký tự càng tốt (tất cả các ký tự đều được phép) cho đến khi bạn đạt đến hai dòng mới". Kết quả (không có dòng mới) được đưa vào nhóm đầu tiên.
  • [A-Z]+\ncó nghĩa là "trận đấu như nhiều trường hợp chữ hoa càng tốt cho đến khi bạn đạt được một dòng mới. Điều này định nghĩa những gì tôi sẽ gọi một textline .
  • ((?:textline)+) phương tiện phù hợp với một hoặc nhiều textlines nhưng không đưa từng dòng trong một nhóm. Thay vào đó, hãy đặt tất cả các textlines trong một nhóm.
  • Bạn có thể thêm cuối cùng \ntrong biểu thức chính quy nếu bạn muốn thực thi một dòng mới kép ở cuối.
  • Ngoài ra, nếu bạn không chắc chắn về loại dòng mới bạn sẽ nhận được ( \nhoặc \rhoặc \r\n) thì chỉ cần sửa biểu thức chính quy bằng cách thay thế mọi lần xuất hiện của \nbằng (?:\n|\r\n?).

1
match () chỉ trả về một kết quả phù hợp, ở đầu văn bản đích, nhưng OP cho biết sẽ có hàng trăm kết quả phù hợp trên mỗi tệp. Tôi nghĩ bạn sẽ muốn finditer () thay thế.
Alan Moore

6

Nếu mỗi tệp chỉ có một chuỗi aminoacid, tôi sẽ không sử dụng biểu thức chính quy. Chỉ một cái gì đó như thế này:

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

Chắc chắn là cách dễ nhất nếu chỉ có một cái và nó cũng khả thi với nhiều cái khác, nếu thêm một số logic nữa. Tuy nhiên, có khoảng 885 protein trong tập dữ liệu cụ thể này và tôi cảm thấy rằng một regex sẽ có thể xử lý điều này.
Ngày

4

tìm thấy:

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

\ 1 = some_varying_text

\ 2 = dòng của tất cả CHỮ HOA

Chỉnh sửa (bằng chứng rằng điều này hoạt động):

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])

Thật không may, cụm từ thông dụng này cũng sẽ khớp với các nhóm chữ viết hoa được phân tách bằng các dòng trống. Nó có thể không phải là một vấn đề lớn mặc dù.
MiniQuark

Có vẻ như coonj thích tệp FASTA. ;)
Andrew Dalke

4

Sau đây là một biểu thức chính quy khớp với một khối văn bản nhiều dòng:

import re
result = re.findall('(startText)(.+)((?:\n.+)+)(endText)',input)

1

Quyền của tôi.

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 )

Tại thời điểm này, bạn có someVaryingText dưới dạng một chuỗi và các axit dưới dạng danh sách các chuỗi. Bạn có thể làm "".join( acids )để tạo một chuỗi duy nhất.

Tôi thấy điều này ít bực bội hơn (và linh hoạt hơn) so với regexes nhiều dòng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.