Python özü kalıbı eşleşmeleri


129

Python 2.7.1 Bir kalıbın içindeki kelimeleri çıkarmak için python düzenli ifadesini kullanmaya çalışıyorum

Buna benzeyen bir ipim var

someline abc
someother line
name my_user_name is valid
some more lines

"My_user_name" kelimesini çıkarmak istiyorum. Gibi bir şey yapıyorum

import re
s = #that big string
p = re.compile("name .* is valid", re.flags)
p.match(s) #this gives me <_sre.SRE_Match object at 0x026B6838>

Kullanıcı_adımı şimdi nasıl çıkarabilirim?

Yanıtlar:


159

Normal ifadeden yakalamanız gerekir. searchkalıp için, eğer bulunursa, kullanarak dizeyi alın group(index). Geçerli kontrollerin yapıldığını varsayarsak:

>>> p = re.compile("name (.*) is valid")
>>> result = p.search(s)
>>> result
<_sre.SRE_Match object at 0x10555e738>
>>> result.group(1)     # group(1) will return the 1st capture.
                        # group(0) will returned the entire matched text.
'my_user_name'

26
Bunun group(0)ilk maç olmadığına emin misin ?
sharshofski

33
Biraz geç, ama hem evet hem hayır. group(0)ilk yakalama grubunu değil, eşleşen metni döndürür. Yakalama grupları ve maçları kafa karıştırıcı görünürken kod yorumu doğru. group(1)ilk yakalama grubunu döndürür.
andrewgu

1
AnladımNameError: name '_' is not defined
Ian G

İkinci satırın okunması gerektiğini düşünüyorum _ = p.search(s). Sonucu ayarladığından bahsettiğini görüyorum _ama kod bunu yansıtmıyor. _ = p.search(s)O ikinci satır için değiştirdim ve işe yarıyor.
Ian G

2
@IanG Üzgünüm, cevabımı güncelleyeceğim. BTW, standart bir python REPL ile, son sonuç adlı özel bir değişkende saklanır _. Başka hiçbir yer dışında geçerli değildir.
UltraInstinct

57

Eşleşen grupları kullanabilirsiniz:

p = re.compile('name (.*) is valid')

Örneğin

>>> import re
>>> p = re.compile('name (.*) is valid')
>>> s = """
... someline abc
... someother line
... name my_user_name is valid
... some more lines"""
>>> p.findall(s)
['my_user_name']

Burada tüm örneklerini almak re.findallyerine kullanıyorum . Kullanarak , eşleşme nesnesindeki gruptan verileri almanız gerekir:re.searchmy_user_namere.search

>>> p.search(s)   #gives a match object or None if no match is found
<_sre.SRE_Match object at 0xf5c60>
>>> p.search(s).group() #entire string that matched
'name my_user_name is valid'
>>> p.search(s).group(1) #first group that match in the string that matched
'my_user_name'

Yorumlarda belirtildiği gibi, normal ifadenizi açgözlü olmayan yapmak isteyebilirsiniz:

p = re.compile('name (.*?) is valid')

normal ifadenizin grubunuzdaki diğerlerini almasına izin vermek yerine , yalnızca aradaki 'name 've sonraki arasındaki şeyleri ' is valid'almak için ' is valid'.


2
Açgözlü olmayan bir eşleşme gerekebilir ... (bir kullanıcı adı birden fazla kelime
olamazsa

@JonClements - Yani (.*?)? Evet, bu mümkün, ancak OP bizi kullanmadıkça gerekli olmasa dare.DOTALL
mgilson

evet - re.findall('name (.*) is valid', 'name jon clements is valid is valid is valid')muhtemelen istenen sonuçları vermeyecek ...
Jon Clements

Bu Python 2.7.1 için çalışmıyor mu? Sadece bir desen nesnesi mi yazdırıyor?
Kannan Ekanath

@CalmStorm - Hangi bölüm çalışmıyor (python2.7.3 üzerinde test ettim)? Kullandığım kısım .groupkabul ettiğiniz cevapla tamamen aynı ...
mgilson

16

Bunun gibi bir şey kullanabilirsiniz:

import re
s = #that big string
# the parenthesis create a group with what was matched
# and '\w' matches only alphanumeric charactes
p = re.compile("name +(\w+) +is valid", re.flags)
# use search(), so the match doesn't have to happen 
# at the beginning of "big string"
m = p.search(s)
# search() returns a Match object with information about what was matched
if m:
    name = m.group(1)
else:
    raise Exception('name not found')

10

Belki bu biraz daha kısadır ve anlaşılması daha kolaydır:

import re
text = '... someline abc... someother line... name my_user_name is valid.. some more lines'
>>> re.search('name (.*) is valid', text).group(1)
'my_user_name'

9

Bir yakalama grubu istiyorsunuz .

p = re.compile("name (.*) is valid", re.flags) # parentheses for capture groups
print p.match(s).groups() # This gives you a tuple of your matches.

9

Dizinin parçalarını yakalamak için grupları ( '('ve ile gösterilir ')') kullanabilirsiniz. Eşleştirme nesnesinin group()yöntemi daha sonra size grubun içeriğini verir:

>>> import re
>>> s = 'name my_user_name is valid'
>>> match = re.search('name (.*) is valid', s)
>>> match.group(0)  # the entire match
'name my_user_name is valid'
>>> match.group(1)  # the first parenthesized subgroup
'my_user_name'

Python 3.6+ sürümünde ayrıca aşağıdakileri kullanmak yerine bir eşleşme nesnesine indeksleyebilirsinizgroup() :

>>> match[0]  # the entire match 
'name my_user_name is valid'
>>> match[1]  # the first parenthesized subgroup
'my_user_name'

6

Grupları kullanmadan yapmanın bir yolu (Python 3.6 veya üstü):

>>> re.search('2\d\d\d[01]\d[0-3]\d', 'report_20191207.xml')[0]
'20191207'

1
Bu Python Regex'e hitap eder, ancak OP'nin özel sorusuna hitap etmez.
Aleister Tanek Javas Mraz

Ayrıca, bu temelde 3.6+ indeksleme sözdiziminden bahseden mevcut cevaplara yeni bir şey eklemez.
Eugene Yarmash

3

Ayrıca bir yakalama grubu kullanabilir (?P<user>pattern)ve gruba bir sözlük gibi erişebilirsiniz match['user'].

string = '''someline abc\n
            someother line\n
            name my_user_name is valid\n
            some more lines\n'''

pattern = r'name (?P<user>.*) is valid'
matches = re.search(pattern, str(string), re.DOTALL)
print(matches['user'])

# my_user_name

1

Görünüşe göre aslında bir isim mengenesi çıkarmaya çalışıyorsunuz, sadece bir eşleşme bulun. Durum buysa, eşleşmeniz için aralık indekslerine sahip olmak yardımcı olur ve kullanmanızı tavsiye ederim re.finditer. Kısayol olarak, namenormal ifadenizin bir kısmının 5 ve is valid9 uzunluğunda olduğunu bilirsiniz , böylece adı çıkarmak için eşleşen metni dilimleyebilirsiniz.

Not - sÖrneğinizde, satır sonu içeren bir dize gibi görünüyor , bu nedenle aşağıda varsayıldığı gibi.

## covert s to list of strings separated by line:
s2 = s.splitlines()

## find matches by line: 
for i, j in enumerate(s2):
    matches = re.finditer("name (.*) is valid", j)
    ## ignore lines without a match
    if matches:
        ## loop through match group elements
        for k in matches:
            ## get text
            match_txt = k.group(0)
            ## get line span
            match_span = k.span(0)
            ## extract username
            my_user_name = match_txt[5:-9]
            ## compare with original text
            print(f'Extracted Username: {my_user_name} - found on line {i}')
            print('Match Text:', match_txt)
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.