J'essaie d'écrire une expression régulière en python pour identifier les instances des phrases "joué pour" et "joue pour" dans un texte, avec la possibilité de trouver des cas où des mots se trouvent entre les deux, par exemple, "joué de la guitare pour ". Je veux seulement que cela trouve la première instance du mot "pour" après "joue" ou "joué", cependant, je ne peux pas trouver comment écrire l'expression régulière.

Le code que j'ai en ce moment est comme ceci:

def play_finder(doc)
    playre = re.compile(r'\bplay[s|e][d]?\b.*\bfor\b\s\b')
    if playre.findall(doc):
        for inst in playre.findall(doc):
            playstr = inst
            print(playstr)

mytext = "He played for four hours last night. He plays guitar for the foo pythers. He won an award for his guitar playing."

play_finder(mytext)

J'aimerais que je puisse retirer deux instances de mytext; "joué pour quatre" et "joue de la guitare pour le".

Au lieu de cela, ce que mon code trouve est:

"Il a joué pendant quatre heures la nuit dernière. Il joue de la guitare pour les foo pythers. Il a remporté un prix pour".

Donc, il saute le premier et le deuxième et ne trouve que le dernier.

Comment puis-je réécrire l'expression régulière pour qu'elle arrête de sauter la première et la deuxième instance de "for" dans la phrase et les identifie toutes les deux?

Edit: Un autre problème est devenu évident pour moi après avoir appliqué une solution qui m'a été proposée. Compte tenu de plusieurs phrases, telles que:

"Il a joué un set de huit heures. On aurait dit qu'il continuait pour toujours."

Je ne veux pas que l'expression régulière identifie "Il a joué un set de huit heures. On aurait dit qu'il continuait" comme correspondant au schéma. Existe-t-il un moyen de l'empêcher de rechercher le "pour" s'il rencontre un arrêt complet?

-1
AdeDoyle 15 avril 2018 à 18:08

3 réponses

Meilleure réponse

Vous pouvez essayer ça,

\bplay(?:s|ed).*?for\b

Démo

Il y a quelques défauts dans l'expression régulière de votre script.

playre = re.compile(r'\bplay[s|e][d]?\b.*\bfor\b\s\b')
  • [s|e]: n'est pas utilisable pour l'expression logique car [] est character class et signifie un seul caractère qu'il autorise
  • .*: greed(*) search semble correspondre à la chaîne de correspondance de longueur maximale possible.
1
Thm Lee 15 avril 2018 à 16:29

Quelqu'un a répondu que j'avais besoin du paresseux .*?, puis a supprimé sa réponse. Je ne sais pas pourquoi, car cela a fonctionné. Par conséquent, le code que j'utilise maintenant est:

(r'\bplay[s|e][d]?\b.*?\bfor\b\s\b')

@ThmLee J'ai essayé votre suggestion:

\bplay(s|ed).*?for\b

Je ne suis (clairement) pas un expert en Regex, mais cela ne semblait pas fonctionner aussi bien. Au lieu de sortir les lignes "joué pour" et "joue de la guitare pour", il ne sort que "s" et "ed".

0
AdeDoyle 15 avril 2018 à 16:10

Vous comprenez mal l'utilisation des crochets. Ils créent une classe de caractères qui correspond à un seul caractère parmi l'ensemble de caractères énumérés entre les crochets. Donc [s|e] correspond à s ou | ou e.

De plus, le mot limite est simplement une assertion. Il correspond si le caractère précédent était un caractère "mot" et le suivant ne l'est pas, ou vice versa; mais cela ne fait pas avancer la position dans la chaîne. Ainsi, par exemple, \s\bfor\b\s est redondant; nous savons déjà que \s correspond aux espaces (qui ne sont pas des mots) et for se compose de caractères de mots. Vous voulez dire simplement \sfor\s parce que les conditions \b supprimées ne changent pas ce qui est mis en correspondance.

Essayer

r'\bplay(?:s|ed)?\s+(?:\w+\s+)??for\s+\w+'

Le (?:\w+\s+)?? permet un seul mot facultatif avant for. Le deuxième point d'interrogation rend la capture non gourmande, c'est-à-dire qu'elle correspond à la chaîne la plus courte possible qui permet toujours à l'expression de correspondre, au lieu de la plus longue. Vous ne voudrez pas autoriser des répétitions illimitées (car alors vous correspondriez par exemple "à un autre jeu avant qu'il ne s'assoie") mais vous pourriez envisager de remplacer le ?? par par exemple {0,3}? pour autoriser jusqu'à trois mots avant "pour".

Nous utilisons (?:...) au lieu de (...) pour rendre les parenthèses de regroupement non capturantes; sinon, findall renverra une liste des sous-matchs capturés plutôt que la correspondance entière.

Le if findall: for findall est une inefficacité mineure; vous avez juste besoin de for match in findall qui répétera simplement zéro fois s'il n'y a pas de correspondance.

Plus généralement, l'utilisation d'expressions régulières pour les modèles grammaticaux de niveau supérieur est très souvent insatisfaisante. Un analyseur grammatical (même un certain type d'analyse peu profonde) est meilleur pour vous dire quand certains mots sont des constituants d'un attribut facultatif ou d'un modificateur pour une phrase nominale, ou quand "play" doit être analysé comme un nom. Considérer

Il a joué - ou plutôt, tapé sur ses doigts et fredonné - pendant trois minutes.

Je joue un autre rôle idiot mais pas complètement scandaleux pour la troisième fois en un an.

Elle joue ce que beaucoup considèrent comme un gameplay offensif pour les Hawks.

Brett joue du hautbois bien qu'il pense que c'est pour les mauviettes.

Certaines pièces sont pour les imbéciles.

0
tripleee 15 avril 2018 à 17:28