2009-02-25 18 views
60

Birden çok satıra yayılan metne karşılık gelirken çalışmak için bir Python ifadesini kullanmakta biraz sorun yaşıyorum. 'Some_Varying_TEXT' parçası ve bunun altında iki satır geliyor büyük metin satırı hepsi: örnek metinÇok satırlı bir metin bloğuyla eşleşen düzenli ifade

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

iki şey yakalamak istiyorum ('\ n' bir satır olduğu) 'dir bir yakalama (daha sonra yeni satır karakterlerini çıkarabilirim). Birkaç yaklaşımlar ile denedim:

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

ve hiçbir şans ile buradaki varyasyonların çok. Sonuncusu, metin satırlarıyla tek tek eşleşiyor gibi görünüyor, aslında istediğim şey bu değil. İlk kısmı yakalayabilirim, sorun değil ama 4-5 satırlık büyük harfli metni yakalayamıyorum. Match.group (1) 'in, _ Varying _ Metin ve grup (2)' nin boş satır ile karşılaşılana kadar line1 + line2 + line3 + vb. Olmasını istiyorum. Birisi meraklıysa, bunun bir proteini oluşturan aminoasit dizisi olması gerekiyordu.

+0

Dosyada ilk satır ve büyük harf dışında başka bir şey var mı? Tüm metni yeni satır karakterlerine bölmek ve ilk öğeyi "some_Varying_TEXT" olarak almak yerine neden regex kullanacağınızdan emin değilim. – UncleZeiv

+2

evet, regex bunun için yanlış bir araçtır. – hop

+0

Örnek metninizde lider '> karakteri yoktur. Olmalı mı? – MiniQuark

cevap

81

bu deneyin:

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

En büyük sorun linefeeds maç için ^ ve $ çapa bekliyorsanız olduğunu düşünüyorum, ama yok. Çok satırlı modda, ^,'u bir satırsonu ile hemen ile eşleştirir ve $, numaralı telefondan numaralı telefona yeni bir satır eklenir.

Ayrıca, bir satırsonu satır besleme (\ n), bir satır başı (\ r) veya bir satır başı + satır beslemesinden (\ r \ n) oluşabileceğini de unutmayın. Eğer hedef metin yalnızca linefeeds kullandığı değil eminseniz, sen regex bu daha kapsayıcı sürümünü kullanmalıdır: BTW

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

, buradan dotall değiştirici kullanmak istemiyoruz; noktanın, yeni satırları hariç, ile eşleştiğine güveniyorsunuz.

+0

Bu normal ifadenin, boş bir ikinci satıra sahip herhangi bir metin dosyasıyla eşleşmesini istemiyorsanız, ikinci noktayı normal ifadede [A-Z] ile değiştirmek isteyebilirsiniz. ;-) – MiniQuark

+0

Benim izlenimim, hedef dosyaların boş (boş) ve boş olmayan satırların belirli (ve yinelenen) modellerine uymasıdır, bu yüzden [AZ] belirtmek gerekli olmamalıdır, ancak muhtemelen zarar vermez ya da. –

+0

Bu çözüm çok güzel çalıştı. Bir yana, özür dilerim, çünkü durumu yeterince açıklığa kavuşturamadım (ve ayrıca bu cevabın gecikmesi için). Yardım ettiğin için teşekkür ederim! – Jan

1

bulmak:

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

\ 1 = some_varying_text tüm CAPS

\ 2 = hatları

Düzenleme (bu çalıştığını kanıtı):

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

Bana yanlış görünüyor. Bunu test ettin mi? – Triptych

+0

Bu, sizin için bazı kod ekledim. –

+0

Maalesef bu düzenli ifade, boş satırlarla ayrılmış büyük harf gruplarıyla da eşleşecektir. Gerçi büyük bir anlaşma olmayabilir. – MiniQuark

14

Bu çalışacak:

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

bu düzenli ifade hakkında bazı açıklamalar yararlı olabilir: (^) anlamına İlk karakter "Bir satırın başında başlayan" ^(.+?)\n\n((?:[A-Z]+\n)+)

  • . Yeni satırın kendisiyle eşleşmediğine dikkat edin ($ için de aynıdır: "yeni satırdan hemen önce" anlamına gelir, ancak yeni satırın kendisiyle eşleşmez).
  • O zaman (.+?)\n\n, "iki yeni satıra ulaşana kadar mümkün olduğunca az karakter (tüm karakterlere izin verilir)" anlamına gelir. Sonuç (yeni satırlar olmadan) ilk gruba konur.
  • [A-Z]+\n vasıta "mümkün olduğunca maç gibi birçok büyük harf harf, bir satırbaşı ulaşana kadar. Bu benim bir TextLine öğesini diyecegimiz tanımlar.
  • ((?:TextLine)+) maç bir veya daha fazla textlines demektir ama yapamaz bir gruptaki her satırı koydu. Bunun yerine, bir grupta tümtextlines koydu. Eğer bir çift yeni satır uygulamak istiyorsanız
  • düzenli ifadede nihai \n ekleyebilir sonunda.Ayrıca
  • , size (\n veya \r veya \r\n) sonra sadece (?:\n|\r\n?) tarafından \n her olay değiştirerek normal ifade düzeltmek alacak yeni satır ne tür hakkında emin değilseniz.
+0

match(), hedef metnin en başına yalnızca bir eşleşme döndürür, ancak OP dosya başına yüzlerce eşleşme olacağını belirtir. Bence finditer() yerine istersiniz. –

+1

@Alan: Sadece sabit, teşekkürler. – MiniQuark

1

Tercihim. Eğer bir dize ve dizeleri liste olarak asitleri gibi someVaryingText var Bu noktada

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) 

. Tek bir dize oluşturmak için "".join(acids)'u yapabilirsiniz.

Çok satırlı regeekslerden daha az rahatsız edici (ve daha esnek) buluyorum.

4

Her dosyada yalnızca bir tane aminoasit dizisi varsa, normal ifadeleri kullanmazdım. Böyle bir şey:

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

Sadece bir tane olsaydı en kolay yolu, daha fazla mantık eklenirse, daha fazlası ile de uygulanabilir. Ancak bu spesifik veri tabanında yaklaşık 885 protein var ve bunu bir düzenli ifadenin halledebileceğini hissettim. – Jan