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.