Je veux faire correspondre la dernière occurrence d'un motif simple dans une chaîne, par exemple.
list = re.findall(r"\w+ AAAA \w+", "foo bar AAAA foo2 AAAA bar2")
print "last match: ", list[len(list)-1]
cependant, si la chaîne est very long, une liste énorme de correspondances est générée. Existe-t-il un moyen plus direct de faire correspondre la deuxième occurrence de "AAAA" ou dois-je utiliser cette solution de contournement?
vous pouvez utiliser $
qui indique le caractère de fin de ligne:
>>> s = """foo bar AAAA
foo2 AAAA bar2"""
>>> re.findall(r"\w+ AAAA \w+$", s)
['foo2 AAAA bar2']
En outre, notez que list
est un nom incorrect pour votre variable, car il ombrage le type intégré. Pour accéder au dernier élément d'une liste, vous pouvez simplement utiliser [-1]
index:
>>> lst = [2, 3, 4]
>>> lst[-1]
4
Vous pouvez éviter de créer une liste simplement en parcourant tous les matchs et en conservant le dernier match:
for match in re.finditer(r"\w+ AAAA \w+", "foo bar AAAA foo2 AAAA bar2"):
pass
Ensuite, match
conserve la dernière correspondance et fonctionne pour toutes les combinaisons de pattern et chaînes recherchées . Vous voudrez peut-être définir d'abord match
sur None
, car s'il n'y a pas de correspondance, match
ne sera défini sur aucune valeur.
Je n'étais pas sûr si votre regex originale vous donnerait ce que vous vouliez. Désolé si je suis en retard pour faire la fête .. Mais cela peut aussi être utile pour d’autres.
import re
p = r"AAAA(?=\s\w+)" #revised per comment from @Jerry
p2 =r"\w+ AAAA \w+"
s = "foo bar AAAA foo2 AAAA bar2"
l = re.findall(p, s)
l2 = re.findall(p2, s)
print('l: {l}'.format(l=l))
#print(f'l: {l}') is nicer, but online interpreters sometimes don't support it.
# https://www.onlinegdb.com/online_python_interpreter
#I'm using Python 3.
print('l2: {l}'.format(l=l2))
for m in re.finditer(p, s):
print(m.span())
#A span of (n,m) would really represent characters n to m-1 with zero based index
#So.(8,12):
# => (8,11: 0 based index)
# => (9th to 12th characters conventional 1 based index)
print(re.findall(p, s)[-1])
Les sorties:
l: ['AAAA', 'AAAA']
l2: ['bar AAAA foo2']
(8, 12)
(18, 22)
AAAA
La raison pour laquelle vous obtenez deux résultats ici au lieu d'un dans l'original est la sauce spéciale (?=)
.
Cela s'appelle un lookahead positif. Cela ne fait pas 'consommer' (c'est-à-dire que le curseur avance), lorsque la correspondance est trouvée lors de l'évaluation de la regex. Donc, il revient après l'appariement.
Bien que les points de référence positifs figurent entre parenthèses, ils agissent également comme un groupe non capturé.
Ainsi, même si un motif correspond, les résultats omit la séquence environnante de caractères alphanumériques représentée par le \w+
et les espaces intermédiaires, \s
dans mon exemple -representing [ \t\n\r\f\v]
. (Plus ici )
Donc, je ne récupère que AAAA à chaque fois.
p2
représente le motif original du code de @SDD, la personne qui pose la question.
foo2
est consommé, avec ce modèle afin que le second AAAA ne corresponde pas, car le curseur était trop avancé, lorsque le moteur des expressions rationnelles reprend à sa deuxième itération de correspondance.
Je vous recommande de regarder les vidéos Youtube de Moondra si vous voulez creuser plus profondément.
Il a réalisé une série très complète de 17 parties sur Python Regex, débutant ici
Voici un lien vers un interpréteur Python en ligne)
Il n'y a pas de fonctionnalité de bibliothèque re
intégrée qui prend en charge l'analyse syntaxique de chaîne de droite à gauche. La chaîne d'entrée est uniquement recherchée pour un motif de gauche à droite.
Il existe cependant un module PyPi regex _ qui prend en charge cette fonctionnalité. Il s'agit de l'indicateur regex.REVERSE
ou de sa variante intégrée, (?r)
:
s="foo bar AAAA foo2 AAAA bar2"
print(regex.search(r"(?r)\w+ AAAA \w+$", s).group())
# => foo2 AAAA bar2
Avec le module re
, il est possible d’atteindre rapidement la fin de la chaîne à l’aide de la construction ^[\s\S]*
et de laisser le retour en arrière rechercher le motif que vous souhaitez intégrer dans un groupe séparé. Toutefois, le retour en arrière peut engloutir une partie de la correspondance (car il cessera de produire plus de texte une fois que tous les modèles suivants auront été mis en correspondance). Si le texte est trop volumineux et qu’il n’ya pas de correspondance, le retour en arrière peut devenir catastrophique. Utilisez cette astuce uniquement si votre chaîne d'entrée correspond toujours, ou si elle est courte et que le modèle personnalisé ne s'appuie pas beaucoup sur le retour arrière:
print(re.search(r"(?:^[\s\S]*\W)?(\w+ AAAA \w+)$", s).group(1))
# => foo2 AAAA bar2
Ici, (?:^[\s\S]*\W)?
correspond à une séquence facultative d'un début de chaîne, tout caractère 0 ou plus suivi d'un caractère autre que Word (\W
). Il est nécessaire d'ajouter \W
pour que le retour en arrière soit renvoyé au caractère autre que Word. Ce caractère doit être facultatif, car la correspondance peut commencer au début de la chaîne.
Voir la démo Python .
Un autre moyen rapide consiste à utiliser search
et group
:
>>> re.search('\w+ AAAA \w+$',"foo bar AAAA foo2 AAAA bar2").group(0)
'foo2 AAAA bar2'
Il utilise le modèle \w+ AAAA \w+$
, qui obtient la dernière occurrence de 'AAAA'
avec ses mots adjacents, tous utilisant \w+
(deux fois) et $
(une fois).
Une fois le filtrage effectué, vous devrez utiliser la méthode _sre.SRE_Match.group
pour obtenir la valeur d’appartenance de l’objet _sre.SRE_Match
et, bien sûr, le groupe zeroth (premier), sachant que search
ne conserve qu’une correspondance (le zeroth).
>>> timeit.timeit(lambda: re.findall(r"\w+ AAAA \w+$", s),number=1000000) # SilentGhost
5.783595023876842
>>> timeit.timeit('import re\nfor match in re.finditer(r"\w+ AAAA \w+", "foo bar AAAA foo2 AAAA bar2"):pass',number=1000000) # tzot
5.329235373691631
>>> timeit.timeit(lambda: re.search('\w+ AAAA \w+$',"foo bar AAAA foo2 AAAA bar2").group(0),number=1000000) # mine (U9-Forward)
5.441731174121287
>>>
Je teste tous les timings en utilisant le module timeit
, et je fabrique également number=1000000
, donc cela prend beaucoup plus de temps.