2009-02-25 26 views
60

Tôi đang gặp một chút rắc rối khi nhận được một regex Python hoạt động khi khớp với văn bản mở rộng nhiều dòng. Các văn bản ví dụ là ('\ n' là một dòng mới)Cụm từ thông dụng khớp với khối văn bản nhiều dòng

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 điều: phần 'some_Varying_TEXT', và tất cả các dòng văn bản chữ hoa mà đến hai dòng 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). Tôi đã thử với một vài phương pháp:

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 Quy chế này không có may mắn. Người cuối cùng dường như phù hợp với từng dòng văn bản, đó không phải là những gì tôi thực sự muốn. Tôi có thể nắm bắt phần đầu tiên, không vấn đề gì, nhưng tôi dường như không thể bắt được 4-5 dòng chữ hoa văn. Tôi muốn match.group (1) thành một số _ Thay đổi _ Văn bản và nhóm (2) thành line1 + line2 + line3 + etc cho đến khi gặp phải dòng trống.

Nếu ai đó tò mò, nó được coi là một chuỗi các amino acid tạo thành một protein.

+0

Có gì khác trong tệp bên cạnh dòng đầu tiên và văn bản chữ hoa không? Tôi không chắc chắn lý do tại sao bạn sẽ sử dụng regex thay vì tách tất cả văn bản ở ký tự dòng mới và lấy phần tử đầu tiên là "some_Varying_TEXT". – UncleZeiv

+2

có, regex là công cụ sai cho việc này. – hop

+0

Văn bản mẫu của bạn không có ký tự '>' hàng đầu. Phải không? – MiniQuark

Trả lời

81

Hãy thử điều 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 các ^$ neo để phù hợp với linefeeds, nhưng họ thì không. Ở chế độ nhiều dòng, ^ khớp vị trí ngay lập tức sau dòng mới và $ khớp với vị trí ngay lập tức trước dòng mới.

Xin lưu ý rằng, một dòng mới có thể bao gồm một dòng cấp (\ n), một vận chuyển trở lại (\ r), hoặc một dòng vận chuyển trở lại + dòng (\ r \ n). Nếu bạn không chắc chắn rằng văn bản mục tiêu của bạn chỉ sử dụng linefeeds, bạn nên sử dụng phiên bản bao gồm hơn này của regex:

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

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

+0

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

+0

Ấn tượng của tôi là các tệp đích sẽ phù hợp với một mẫu (và lặp lại) xác định của các dòng trống và 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 bị tổn thương , hoặc. –

+0

Giải pháp này hoạt động rất tốt. Là một sang một bên, tôi xin lỗi, vì tôi rõ ràng đã không làm rõ tình hình đủ (và cũng cho độ trễ của câu trả lời này). Cảm ơn bạn đã giúp đỡ! – Jan

1

tìm:

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

\ 1 = some_varying_text

\ 2 = dòng tất cả CAPS

Chỉnh sửa (bằng chứng cho thấy việc này):

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

Có vẻ sai với tôi. Bạn đã thử nghiệm này? – Triptych

+0

Có, tôi đã thêm một số mã cho bạn. –

+0

Thật không may, biểu thức chính quy này cũng sẽ khớp với các nhóm chữ cái 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

14

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ề biểu thức chính quy này có thể có ích: ^(.+?)\n\n((?:[A-Z]+\n)+)

  • Ký tự đầu tiên (^) có nghĩa là "bắt đầu vào đầu của một dòng". Lưu ý rằng nó không khớp với chính dòng mới (giống 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\n có nghĩa là "khớp với ít ký tự nhất có thể (tất cả ký tự đượ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]+\n có nghĩa là "trận đấu càng nhiều chữ hoa chữ càng tốt cho đến khi bạn đạt được một dòng mới. Điều này xác định những gì tôi sẽ gọi một textline .
  • ((?:textline)+) nghĩa trận đấu 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 một thức \n trong biểu thức chính quy nếu bạn muốn thực thi một dòng mới đôi cuối cùng.
  • Ngoài ra, nếu bạn không chắc chắn về những gì loại newline bạn sẽ nhận được (\n hoặc \r hay \r\n) sau đó chỉ cần sửa chữa các biểu hiện thường xuyên bằng cách thay thế mỗi lần xuất hiện của \n bởi (?:\n|\r\n?).
+0

match() chỉ trả lại một kết quả phù hợp, ngay từ đầu của văn bản đích, nhưng OP cho biết sẽ có hàng trăm kết quả phù hợp cho mỗi tệp. Tôi nghĩ rằng bạn sẽ muốn finditer() thay thế. –

+1

@Alan: Chỉ cần cố định, cảm ơn. – MiniQuark

1

Tùy chọ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 làm chuỗi và các axit dưới dạng danh sách chuỗi. Bạn có thể làm "".join(acids) để tạo một chuỗi.

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

4

Nếu mỗi tệp chỉ có một chuỗi các aminoacids, tôi sẽ không sử dụng biểu thức chính quy nào cả. Chỉ cần 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 
+0

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

Các vấn đề liên quan