Dopasowania wzorca wyodrębniania Pythona


129

Python 2.7.1 Próbuję użyć wyrażenia regularnego w języku Python, aby wyodrębnić słowa wewnątrz wzorca

Mam taki sznurek

someline abc
someother line
name my_user_name is valid
some more lines

Chcę wyodrębnić słowo „my_user_name”. Robię coś takiego

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>

Jak mogę teraz wyodrębnić my_user_name?

Odpowiedzi:


159

Musisz przechwycić z wyrażenia regularnego. searchdla wzorca, jeśli zostanie znaleziony, pobierz ciąg przy użyciu group(index). Zakładając, że przeprowadzane są prawidłowe sprawdzenia:

>>> 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
Jesteś pewien, że to nie group(0)na pierwszy mecz?
sharshofski

33
Trochę późno, ale zarówno tak, jak i nie. group(0)zwraca dopasowany tekst, a nie pierwszą grupę przechwytywania. Komentarz do kodu jest poprawny, chociaż wydaje się, że dezorientujesz grupy przechwytywania i dopasowania. group(1)zwraca pierwszą grupę przechwytywania.
andrewgu

1
DostajęNameError: name '_' is not defined
Ian G

Myślę, że powinienem przeczytać Twoją drugą linijkę _ = p.search(s). Widzę, że wspomina o ustawieniu wyniku na, _ale kod tego nie odzwierciedla. Zmieniłem _ = p.search(s)na drugą linię i działa.
Ian G

2
@IanG Przepraszam, zaktualizuję odpowiedź. Przy okazji, w przypadku standardowej instrukcji REPL w Pythonie, ostatni wynik jest przechowywany w specjalnej zmiennej o nazwie _. Nie obowiązuje na zewnątrz nigdzie indziej.
UltraInstinct

57

Możesz użyć pasujących grup:

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

na przykład

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

Tutaj używam re.findallzamiast re.searchpobierania wszystkich wystąpień my_user_name. Używając re.search, musisz pobrać dane z grupy w obiekcie dopasowania:

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

Jak wspomniano w komentarzach, możesz chcieć, aby Twoje wyrażenie regularne nie było chciwe:

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

aby podnieść tylko rzeczy pomiędzy 'name 'a następną ' is valid'(zamiast pozwalać twojemu wyrażeniu regularnemu na wybieranie innych ' is valid'w twojej grupie.


2
Możliwe, że wymagany jest niechciany mecz ... (chyba że nazwa użytkownika może składać się z wielu słów ...)
Jon Clements

@JonClements - Masz na myśli (.*?)? Tak, to możliwe, chociaż nie jest to konieczne, chyba że używamy OPre.DOTALL
mgilson

tak - re.findall('name (.*) is valid', 'name jon clements is valid is valid is valid')prawdopodobnie nie przyniesie pożądanych rezultatów ...
Jon Clements

To nie działa w przypadku Pythona 2.7.1? Po prostu drukuje obiekt wzorca?
Kannan Ekanath

@CalmStorm - Która część nie działa (testowałem na pythonie2.7.3)? Część, w której używam, .groupjest dokładnie taka sama, jak odpowiedź, którą zaakceptowałeś ...
mgilson

16

Możesz użyć czegoś takiego:

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

Może to trochę krótsze i łatwiejsze do zrozumienia:

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

Chcesz grupę przechwytywania .

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

Możesz użyć grup (oznaczonych '('i ')') do przechwytywania części ciągu. Metoda obiektu match group()podaje następnie zawartość grupy:

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

W Pythonie 3.6+ możesz także indeksować do obiektu dopasowania zamiast używać group():

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

6

Oto sposób na zrobienie tego bez używania grup (Python 3.6 lub nowszy):

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

1
Dotyczy to Python Regex, ale nie dotyczy konkretnego pytania OP.
Aleister Tanek Javas Mraz

Poza tym w zasadzie nie dodaje to nic nowego do istniejących odpowiedzi, wspominając o składni indeksowania 3.6+.
Eugene Yarmash

3

Możesz także użyć grupy przechwytywania (?P<user>pattern)i uzyskać dostęp do grupy jak do słownika 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

Wygląda na to, że tak naprawdę próbujesz wydobyć imię imadło, po prostu znajdź dopasowanie. W takim przypadku posiadanie indeksów zakresu dla dopasowania jest pomocne i polecam użycie re.finditer. Jako skrót wiesz, że nameczęść twojego wyrażenia regularnego ma długość 5, a is validdługość 9, więc możesz wyciąć pasujący tekst, aby wyodrębnić nazwę.

Uwaga - w Twoim przykładzie wygląda to tak, jakby sto był ciąg znaków ze znakami końca linii, więc tak właśnie przyjęto poniżej.

## 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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.