2013-10-06 3 views
0

J'essaie de comprendre comment faire regex capturer un tas d'éléments qui viennent après une chose en particulier. J'utilise Python pour ça. Un exemple de quelque chose comme ceci serait d'utiliser le texte B <4>.<5> <6> A <1> m<2> . <3> avec l'intention de capturer seulement 1, 2, et 3. Je pensais qu'une expression régulière comme A.*?<(.+?)> fonctionnerait, mais il caputures seulement le final 3 en utilisant Python re.findall. Puis-je obtenir de l'aide avec ça?Regex capturer plusieurs phrases après un

+0

Vous essayez de capturer le 1, 2 et 3 en tant que groupes séparés ou un groupe contenant tous? – BrenBarn

+0

duplication possible de [plusieurs groupes Python regex] (http://stackoverflow.com/questions/4963691/python-regex-multiple-groups) – BrenBarn

+0

Peu importe pour moi, mais j'essayais à l'origine de les faire séparément groupes. – Paul

Répondre

1

Il serait plus facile avec un regard en arrière de largeur variable, mais un autre peut-être pour vous assurer qu'il n'y a pas A après les pièces que vous êtes correspondant de sorte que vous pouvez utiliser quelque chose comme:

re.findall(r'<(.+?)>(?![^A]*A[^A]*$)', 'B <4>.<5> <6> A <1> m<2> . <3>') 

Mais est ici un problème ici ... (.+?) accepte quoi que ce soit qui peut casser ce que vous cherchez. Vous pouvez utiliser une classe annulée: [^>]+ au lieu de .+?.

Cela signifie:

re.findall(r'<([^>]+)>(?![^A]*A[^A]*$)', 'B <4>.<5> <6> A <1> m<2> . <3>') 

regex101 demo

(?![^A]*A[^A]*$) assure qu'il n'y a pas A avant la partie que vous capturez.

(?! ...) est une vue d'ensemble négative qui fait échouer la correspondance si ce qui est à l'intérieur correspond.

[^A]* correspond à tout caractère sauf A

$ correspond à la fin de la chaîne.

1

En l'état actuel, votre code correspond au texte entre < et > qui vient après A suivi de zéro caractère ou plus. En outre, la seule partie de votre texte qui remplit cette condition est <1> (c'est pourquoi c'est tout ce qui est retourné).

Il y a plusieurs façons de résoudre ce problème, mais je pense que le plus simple est de première division sur A, puis utilisez <(.+?)>:

>>> from re import findall, split 
>>> text = 'B <4>.<5> <6> A <1> m<2> . <3>' 
>>> text = split('A', 'B <4>.<5> <6> A <1> m<2> . <3>') 
>>> text 
['B <4>.<5> <6> ', ' <1> m<2> . <3>'] 
>>> text = text[1] 
>>> text 
' <1> m<2> . <3>' 
>>> text = findall('<(.+?)>', text) 
>>> text 
['1', '2', '3'] 
>>> 

Au-dessus est une démonstration étape par étape. Ci-dessous le code que vous voulez:

>>> text = 'B <4>.<5> <6> A <1> m<2> . <3>' 
>>> findall('<(.+?)>', split('A', text)[1]) 
['1', '2', '3'] 
>>> 
+0

N'est-ce pas l'inverse? (?. +) au lieu de (. +?)? Je pense que vous essayez de faire une recherche "non gourmande". Ai-je raison?. EDIT: Vous avez raison. C'est (. +?) Selon la référence de Python. –

+0

Non. La façon dont je l'ai mis en fait un match non-gourmand. – iCodez

+0

@ RobsonFrança '(?. +)' N'est pas une regex valide. '(?:. +)' peut-être, mais pas '(?. +)'. – Jerry

2

Le regex module (va remplacer re dans les prochains pythons) soutient variables assertions arrières, ce qui le rend assez facile:

s = "B <4>.<5> <6> A23 <1> m<2> . <3>" 

import regex 
print regex.findall(r'(?<=A\d+.*)<.+?>', s) 
# ['<1>', '<2>', '<3>'] 

(je suis en utilisant à la place A\d+ de seulement A pour rendre la chose intéressante). Si vous êtes lié au stock re, vous êtes obligé de solutions de contournement laides comme ceci:

import re 
print re.findall(r'(<[^<>]+>)(?=(?:.(?!A\d+))*$)', s) 
# ['<1>', '<2>', '<3>'] 

ou pré-séparation:

print re.findall(r'<.+?>', re.split(r'A\d+', s)[-1]) 
Questions connexes