Corrispondenze del modello di estrazione Python


129

Python 2.7.1 Sto cercando di usare l'espressione regolare di Python per estrarre parole all'interno di un modello

Ho una stringa che assomiglia a questa

someline abc
someother line
name my_user_name is valid
some more lines

Voglio estrarre la parola "my_user_name". Faccio qualcosa del genere

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>

Come estraggo my_user_name ora?

Risposte:


159

Devi catturare da regex. searchper il modello, se trovato, recuperare la stringa usando group(index). Supponendo che vengano eseguiti controlli validi:

>>> 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
Sei sicuro che non sia group(0)per la prima partita?
— sharshofski,

33
Un po 'tardi, ma sia sì che no. group(0)restituisce il testo corrispondente, non il primo gruppo di acquisizione. Il commento del codice è corretto, mentre sembra che tu confonda i gruppi di acquisizione e le corrispondenze. group(1)restituisce il primo gruppo di acquisizione.
— Andrewgu,

1
RicevoNameError: name '_' is not defined
— Ian G

La tua seconda riga penso che dovrei leggere _ = p.search(s). Vedo che menziona l'impostazione del risultato _ma il codice non lo riflette. Ho cambiato _ = p.search(s)per quella seconda riga e funziona.
— Ian G,

2
@IanG Mi dispiace, aggiornerò la mia risposta. A proposito, con un REPL standard di Python, l'ultimo risultato è memorizzato in una variabile speciale chiamata _. Non è valido altrove.
— UltraInstinct,

57

Puoi utilizzare i gruppi corrispondenti:

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

per esempio

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

Qui uso re.findallinvece di re.searchottenere tutte le istanze di my_user_name. Utilizzando re.search, avresti bisogno di ottenere i dati dal gruppo sull'oggetto match:

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

Come menzionato nei commenti, potresti voler rendere il tuo regex non avido:

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

per raccogliere solo le cose tra 'name 'e il successivo ' is valid'(piuttosto che consentire al tuo regex di raccogliere altri ' is valid'nel tuo gruppo.


2
È possibile che sia richiesta una partita non golosa ... (a meno che un nome utente non possa essere composto da più parole ...)
— Jon Clements

@JonClements - Intendi (.*?)? Sì, è possibile, anche se non necessario, a meno che OP non ci re.DOTALL
— stia

sì - re.findall('name (.*) is valid', 'name jon clements is valid is valid is valid')probabilmente non produrrà i risultati desiderati ...
— Jon Clements

Questo non funziona con Python 2.7.1? Stampa solo un oggetto modello?
— Kannan Ekanath,

@CalmStorm - Quale parte non funziona (ho testato su python2.7.3)? La parte in cui uso .groupè esattamente la stessa della risposta che hai accettato ...
— mgilson

16

Puoi usare qualcosa del genere:

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

Forse è un po 'più breve e più facile da capire:

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

Vuoi un gruppo di acquisizione .

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

È possibile utilizzare i gruppi (indicati con '('e ')') per acquisire parti della stringa. Il group()metodo dell'oggetto match ti dà quindi i contenuti del gruppo:

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

In Python 3.6+ puoi anche indicizzare in un oggetto match invece di usare group():

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

6

Ecco un modo per farlo senza usare i gruppi (Python 3.6 o successivo):

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

1
Questo riguarda Python Regex, ma non affronta la domanda specifica di OP.
— Aleister Tanek Javas Mraz,

Inoltre, questo non aggiunge nulla di nuovo alle risposte esistenti che menzionano la sintassi di indicizzazione 3.6+.
— Eugene Yarmash,

3

Puoi anche utilizzare un gruppo di acquisizione (?P<user>pattern)e accedere al gruppo come un dizionario 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

Sembra che tu stia effettivamente cercando di estrarre un nome vice semplicemente trovare una corrispondenza. In questo caso, avere indici di span per la tua partita è utile e ti consiglio di usarlo re.finditer. Come scorciatoia, sai che la nameparte del tuo regex è lunghezza 5 e is validlunghezza 9, quindi puoi tagliare il testo corrispondente per estrarre il nome.

Nota - Nel tuo esempio, sembra che ssia una stringa con interruzioni di riga, quindi è quello che si presume di seguito.

## 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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.