2011-06-08 2 views
2

J'essaie de trouver certains mots-clés dans une chaîne avec python. La chaîne est quelque chose comme ceci:python regexp pour quelques milliers de mots

A was changed from B to C 

tous J'essaie de trouver est le « à C » partie, où C est l'un des plusieurs milliers de mots.

Ce code construit la chaîne de regexp:

pre_pad = 'to ' 
regex_string = None 
for i in words: 
    if regex_string == None: 
     regex_string = '\\b%s%s(?!-)(?!_)\\b' %(pre_pad, i) 
    else: 
     regex_string = regex_string + '|\\b%s%s(?!-)(?!_)\\b' %(pre_pad, i) 

Et plus tard, je le veux

matches = [] 
for match in re.finditer(r"%s" %regex_string, text): 
     matches.append([match, MATCH_TYPE]) 

Ce code fonctionne sur linux, mais se bloque sur macos avec « Pris OverflowError tout en rendant: régulier dépassement de la limite de taille du code d'expression "

Je réalise que le regex_string est très longue et que c'est la cause du problème

print regex_string.__len__() 
63574 

comment puis-je résoudre ce problème si ce sera toujours travailler, indépendamment du nombre de mots?

EDIT:

j'oublié de mentionner que le pre_pad est parfois vide: pre_pad = '', donc la recherche de pre_pad premier n'est pas toujours possible. En plus de cela, la raison pour laquelle je construis tout le regex_string d'abord et ensuite le faire correspondre avec les mots est que je dois faire ce correspondant pour plusieurs milliers d'entrées. Si je devais reconstruire la regex_string à chaque fois, cela conduirait à de très mauvaises performances.

Oh, et j'ai besoin de savoir quel mot correspond.

+0

Cela n'aurait jamais dû vous arriver de le faire avec une regex, ce que vous décrivez n'est même pas à distance comme le sont les regex. Il suffit de diviser la chaîne sur les espaces et de parcourir les mots en vérifiant un 'set' ou un' dict' des mots-clés désirés. –

+0

cela ne sera-t-il pas plus lent? – memyself

+2

Pourquoi serait-il plus lent? Par définition, les recherches set et dict sont extrêmement rapides (et doivent l'être, pratiquement tout ce que vous faites en Python dépend d'une dict), et je divise une chaîne de 28Mo en une liste de 4 millions d'éléments en environ 1 seconde. A quel point tes cordes sont-elles gémineuses? L'optimisation prématurée n'achève rien mais gaspille du temps de développement précieux, et finit généralement par vous donner du code sous-optimal de toute façon. –

Répondre

3

Ce n'est pas censé être une tâche que vous pouvez résoudre avec un énorme regexp et attendre de meilleures performances que cela:

pre_pad = 'to ' 
matches = [] 

for i in words: 
    regex_string = '\\b%s%s(?!-)(?!_)\\b' % (pre_pad, i) 
    for match in re.finditer(r"%s" % regex_string, text): 
     matches.append([match, MATCH_TYPE]) 

Aussi, si, après profilage votre code que vous voyez le travail de regexp enchaîné calculer plus vite votre regexp longueur de la chaîne lors de la construction et diviser la tâche complète en 2, 3, 10 pour éviter le débordement.

P.S .:

print len(regex_string) 

est plus pythonique ...

+0

J'ai oublié de mentionner que le pre_pad est parfois vide: pre_pad = '', donc la recherche de pre_pad n'est pas toujours possible. En plus de cela, la raison pour laquelle je construis tout le regex_string d'abord et ensuite la correspondance est contre les mots est que je dois faire ce correspondant pour plusieurs milliers d'entrées. Si je devais reconstruire la regex_string à chaque fois, cela conduirait à de très mauvaises performances. – memyself

+0

@memyself: Je ne peux pas obtenir le problème 'pre_pad' de toute façon ma suggestion était de faire une regex pour chaque mot ou de diviser le gros en morceaux. Ensuite, corrigez le code en conséquence. A propos de très mauvaises performances: ** as-tu profilé pour être sûr de ça **? Quelle est la perte de performance en pourcentage et en temps? – neurino

+0

votre solution s'exécute ~ 1000 fois plus lentement que ma solution. c'est parce que les mots sont quelques milliers de mots. – memyself

0

Le problème mentionné ci-dessus semble se prêter très bien à une solution non-regex.

Vous pouvez également parcourir les correspondances pour r'\b%s(\B+)(?!-)(?!_)\b' % pre_pad et vérifier que le mot correspondant au premier groupe figure dans votre dictionnaire.

+0

Pouvez-vous suggérer un exemple? – Krab

+0

@Krab: Eh bien, faites une analyse linéaire en recherchant 'pre_pad' ('to'), puis vérifiez ce qui est immédiatement avant et après. – NPE

+0

cela ne fonctionnera que si pre_pad n'est pas vide. Mais j'ai aussi des cas où pre_pad est vide. – memyself

0

Je ne suis pas un expert en python, donc ma réponse ne fait pas autorité. Cependant, il me semble que regex n'est pas le meilleur outil dans ce cas.Si la structure de la chaîne

A was changed from B to C 

est fixe, alors est-il pas suffisant pour utiliser le itérer opérateur in sur les mots que vous voulez vérifier:

>>> "to blue" in "A was changed from red to blue" 
True 
1

Vous pouvez extraire C de votre entrée par une simple expression régulière puis rechercher dans une structure optimisée pour la recherche:

  • un arbre
  • liste ordonnée avec b Recherche inaire
  • structure de hachage (comme python de set)

Quelque chose comme

return match_from_regex in set_of_words 
+0

Il s'agit essentiellement de la solution mise à jour d'aix. J'ai raté la mise à jour lorsque j'ai posté ma réponse. – Krab

+0

qui ne fonctionne que pre_pad n'est pas vide. Si pre_pad est vide, alors je dois toujours faire correspondre chaque mot C. – memyself

+0

Quel est le problème? Juste extraire le mot pour vérifier par une regex avec pre_pad vide et ensuite le rechercher. – Krab

1

J'aborder ce problème un peu différemment pour être honnête. Je ferais une carte de mots (que je peux vérifier si le mot existe avec la complexité O (1)). puis recherchez tout "to [\ w] +" regex pour obtenir chaque "to" correspond dans le grand texte. alors pour chaque match, je vérifierais s'il existe dans la carte des mots. Serait beaucoup plus efficace, je suppose.