Correspondances de modèle d'extrait Python

129

Python 2.7.1 J'essaie d'utiliser l'expression régulière python pour extraire des mots à l'intérieur d'un modèle

J'ai une ficelle qui ressemble à ça

someline abc
someother line
name my_user_name is valid
some more lines

Je veux extraire le mot "mon_nom_utilisateur". Je fais quelque chose comme

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>

Comment extraire mon_nom_utilisateur maintenant?

Kannan Ekanath
la source

Réponses:

159

Vous devez capturer à partir de regex. searchpour le modèle, s'il est trouvé, récupérez la chaîne en utilisant group(index). En supposant que des vérifications valides sont effectuées:

>>> 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'
UltraInstinct
la source
26
Etes-vous sûr que ce n'est pas group(0)pour le premier match?
sharshofski
33
Un peu tard, mais oui et non. group(0)renvoie le texte correspondant, pas le premier groupe de capture. Le commentaire du code est correct, alors que vous semblez confondre les groupes de capture et les correspondances. group(1)renvoie le premier groupe de capture.
andrewgu
1
Je reçoisNameError: name '_' is not defined
Ian G
Je pense que votre deuxième ligne devrait être lue _ = p.search(s). Je vois qu'il mentionne la définition du résultat sur _mais le code ne reflète pas cela. J'ai changé _ = p.search(s)pour cette deuxième ligne et cela fonctionne.
Ian G
2
@IanG Je suis désolé, je vais mettre à jour ma réponse. BTW, avec un REPL python standard, le dernier résultat est stocké dans une variable spéciale appelée _. Ce n'est valable nulle part ailleurs.
UltraInstinct
57

Vous pouvez utiliser des groupes correspondants:

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

par exemple

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

Ici, j'utilise re.findallplutôt que re.searchpour obtenir toutes les instances de my_user_name. En utilisant re.search, vous devez obtenir les données du groupe sur l'objet de correspondance:

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

Comme mentionné dans les commentaires, vous voudrez peut-être rendre votre regex non gourmande:

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

pour ne ramasser que les choses entre 'name 'et le suivant ' is valid'(plutôt que de permettre à votre regex d'en prendre d'autres ' is valid'dans votre groupe.

mgilson
la source
2
Il est possible qu'une correspondance non gourmande soit requise ... (sauf si un nom d'utilisateur peut être composé de plusieurs mots ...)
Jon Clements
@JonClements - Vous voulez dire (.*?)? Ouais, c'est possible, mais pas nécessaire, sauf si nous utilisons OPre.DOTALL
mgilson
ouais - re.findall('name (.*) is valid', 'name jon clements is valid is valid is valid')ne donnera probablement pas les résultats souhaités ...
Jon Clements
Cela ne fonctionne pas pour Python 2.7.1? Il imprime juste un objet de modèle?
Kannan Ekanath
@CalmStorm - Quelle partie ne fonctionne pas (j'ai testé sur python2.7.3)? La partie où j'utilise .groupest exactement la même que la réponse que vous avez acceptée ...
mgilson
16

Vous pouvez utiliser quelque chose comme ceci:

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')
Apalala
la source
10

C'est peut-être un peu plus court et plus facile à comprendre:

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'
John
la source
9

Vous voulez un groupe de capture .

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.
Henry Keiter
la source
9

Vous pouvez utiliser des groupes (indiqués par '('et ')') pour capturer des parties de la chaîne. La group()méthode de l'objet match vous donne alors le contenu du groupe:

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

Dans Python 3.6+, vous pouvez également indexer dans un objet de correspondance au lieu d'utiliser group():

>>> match[0]  # the entire match 
'name my_user_name is valid'
>>> match[1]  # the first parenthesized subgroup
'my_user_name'
Eugène Yarmash
la source
6

Voici un moyen de le faire sans utiliser de groupes (Python 3.6 ou supérieur):

>>> re.search('2\d\d\d[01]\d[0-3]\d', 'report_20191207.xml')[0]
'20191207'
loups
la source
1
Cela répond à Python Regex, mais ne répond pas à la question spécifique d'OP.
Aleister Tanek Javas Mraz le
En outre, cela n'ajoute fondamentalement rien de nouveau aux réponses existantes mentionnant la syntaxe d'indexation 3.6+.
Eugene Yarmash le
3

Vous pouvez également utiliser un groupe de capture (?P<user>pattern)et accéder au groupe comme un dictionnaire 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
Ryan Stefan
la source
1

Il semble que vous essayez en fait d'extraire un nom et de trouver simplement une correspondance. Si tel est le cas, il est utile d'avoir des index span pour votre correspondance et je vous recommande de les utiliser re.finditer. En tant que raccourci, vous savez que la namepartie de votre expression régulière est de longueur 5 et la is validlongueur est de 9, vous pouvez donc découper le texte correspondant pour extraire le nom.

Remarque - Dans votre exemple, cela ressemble à une schaîne avec des sauts de ligne, c'est donc ce qui est supposé ci-dessous.

## 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)
homme chic
la source