Çok satırlı bir metin bloğuyla eşleşen normal ifade


106

Birden çok satıra yayılan metinle eşleşirken Python normal ifadesinin çalışmasını sağlamakta biraz sorun yaşıyorum. Örnek metin ('\ n' yeni satırdır)

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

İki şeyi yakalamak istiyorum: 'some_Varying_TEXT' bölümü ve bir yakalamada iki satırın altına gelen büyük harfli metnin tüm satırları (yeni satır karakterlerini daha sonra çıkarabilirim). Birkaç yaklaşımla denedim:

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

ve burada şanssız bir çok varyasyon. Sonuncusu, metin satırlarıyla tek tek eşleşiyor gibi görünüyor, bu gerçekten istediğim şey değil. İlk kısmı yakalayabilirim, sorun değil, ancak 4-5 satırlık büyük harfli metni yakalayamıyorum. Match.group (1) 'in some_Varying_Text olmasını ve group (2)' nin line1 + line2 + line3 + etc olmasını, boş satırla karşılaşılana kadar istiyorum.

Merak eden biri varsa, bunun bir protein oluşturan bir aminoasit dizisi olması gerekir.


Dosyada ilk satır ve büyük harfli metnin dışında başka bir şey var mı? Metnin tamamını yeni satır karakterlerinde bölmek ve ilk öğeyi "bir_Varying_TEXT" olarak almak yerine neden bir normal ifade kullandığınızı bilmiyorum.
UncleZeiv

2
evet, regex bunun için yanlış araçtır.

Örnek metninizin başında bir >karakter yok. Olmalı mı?
MiniQuark

Yanıtlar:


115

Bunu dene:

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

Bence en büyük probleminiz, ^ve $çapaların hat beslemelerine uymasını beklemeniz, ama onlar değil. Çok satırlı modda, bir satırsonu satırının ^hemen ardından$ gelen konumla eşleşir ve satırsonu satırının hemen önündeki konumla eşleşir.

Bir satırsonu satırının satır besleme (\ n), satır başı (\ r) veya satır başı + satır besleme (\ r \ n) içerebileceğini de unutmayın. Hedef metninizin yalnızca satır beslemeleri kullandığından emin değilseniz, normal ifadenin bu daha kapsayıcı sürümünü kullanmalısınız:

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

BTW, burada DOTALL değiştiriciyi kullanmak istemezsiniz; noktanın yeni satırlar dışında her şeyle eşleştiğine güveniyorsunuz .


Bu normal ifadenin hemen hemen herhangi bir metin dosyasıyla boş bir ikinci satırla eşleşmesini istemiyorsanız, normal ifadedeki ikinci noktayı [AZ] ile değiştirmek isteyebilirsiniz. ;-)
MiniQuark

Benim izlenimim, hedef dosyaların belirli (ve yinelenen) bir boş ve boş olmayan satır modeline uyacağıdır, bu nedenle [AZ] belirtmek gerekli değildir, ancak muhtemelen zarar da vermez.
Alan Moore

Bu çözüm çok güzel çalıştı. Bir kenara, durumu yeterince açıklığa kavuşturmadığım için (ve ayrıca bu yanıtın gecikmesi için) özür dilerim. Yardımınız için teşekkürler!
Ocak

23

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 normal ifade hakkında bazı açıklamalar yararlı olabilir: ^(.+?)\n\n((?:[A-Z]+\n)+)

  • İlk karakter ( ^) "satırın başından başlamak" anlamına gelir. Yeni satırın kendisiyle eşleşmediğini unutmayın ($ için aynıdır: "yeni satırdan hemen önce" anlamına gelir, ancak satırsonunun kendisiyle eşleşmez).
  • Bu (.+?)\n\n, "iki yeni satıra ulaşana kadar mümkün olduğunca az karakterle eşleştir (tüm karakterlere izin verilir)" anlamına gelir. Sonuç (satırsonu olmadan) ilk gruba konur.
  • [A-Z]+\n"Bir satırsonu satırına ulaşana kadar olabildiğince çok büyük harf eşleştir. Bu, metin satırı olarak adlandıracağım şeyi tanımlar .
  • ((?:metin çizgisi, bir )+)veya daha fazla metin satırıyla eşleştiği, ancak her satırı bir gruba koymadığı anlamına gelir . Bunun yerine, koymak tüm textlines bir grupta.
  • \nSonunda çift yeni satırı zorlamak istiyorsanız, normal ifadeye bir final ekleyebilirsiniz .
  • Ayrıca, emin (alacak yeni satır ne tür hakkında değilseniz \nya \rya \r\nsadece her olay değiştirerek normal ifade düzeltmek sonra) \ntarafından (?:\n|\r\n?).

1
match (), hedef metnin en başında yalnızca bir eşleşme döndürür, ancak OP dosya başına yüzlerce eşleşme olacağını söyledi. Bunun yerine finditer () isteyeceğinizi düşünüyorum.
Alan Moore

6

Her dosyada yalnızca bir aminoasit dizisi varsa, düzenli ifadeler kullanmam. Sadece bunun gibi 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

Yalnızca bir tane olsaydı kesinlikle en kolay yol ve biraz daha mantık eklenirse, daha fazlasıyla da uygulanabilir. Yine de bu özel veri setinde yaklaşık 885 protein var ve bir normal ifadenin bunu halledebilmesi gerektiğini hissettim.
Ocak

4

bul:

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

\ 1 = bir_değişen_metin

\ 2 = tüm CAPS satırları

Düzenleme (bunun işe yaradığını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])

Ne yazık ki, bu normal ifade, boş satırlarla ayrılmış büyük harf gruplarıyla da eşleşecektir. Yine de büyük bir anlaşma olmayabilir.
MiniQuark

Görünüşe göre coonj FASTA dosyalarını seviyor. ;)
Andrew Dalke

4

Aşağıdaki, çok satırlı bir metin bloğu ile eşleşen normal bir ifadedir:

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

1

Benim tercihim.

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 )

Bu noktada, bir dizi olarak bir Değişken Metin ve dizelerin listesi olarak asitler var. "".join( acids )Tek bir dize yapmak için yapabilirsiniz.

Bunu çok satırlı normal ifadelerden daha az sinir bozucu (ve daha esnek) buluyorum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.