2009-12-20 5 views
1

Après avoir trouvé l'algorithme de remplacement de chaîne le plus rapide en this thread, j'ai essayé de modifier l'un d'entre eux pour répondre à mes besoins, notamment this one par gnibbler. Je vais expliquer à nouveau le problème ici, et quel problème je suis en train de vivre.Expression régulière à fractionner sur un caractère spécifique SEULEMENT si ce caractère n'est pas dans une paire

Dire que j'ai une chaîne qui ressemble à ceci:

str = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog" 

Vous remarquerez beaucoup d'endroits dans la chaîne où il y a une esperluette, suivi d'un caractère (comme « & y » et "& c"). Je dois remplacer ces caractères avec une valeur appropriée que j'ai dans un dictionnaire, comme ceci:

dict = {"y":"\033[0;30m", 
     "c":"\033[0;31m", 
     "b":"\033[0;32m", 
     "Y":"\033[0;33m", 
     "u":"\033[0;34m"} 

solution à l'aide gnibblers fournie dans mon précédent fil, j'ai ce que ma solution actuelle:

myparts = tmp.split('&') 
myparts[1:]=[dict.get(x[0],"&"+x[0])+x[1:] for x in myparts[1:]] 
result = "".join(myparts) 

Cela fonctionne pour remplacer les caractères correctement, et n'échoue pas sur les caractères qui ne sont pas trouvés. Le seul problème avec ceci est qu'il n'y a aucun moyen simple pour effectivement garder une perluète dans la sortie. La meilleure façon que je pouvais penser serait de changer mon dictionnaire pour contenir:

dict = {"y":"\033[0;30m", 
     "c":"\033[0;31m", 
     "b":"\033[0;32m", 
     "Y":"\033[0;33m", 
     "u":"\033[0;34m", 
     "&":"&"} 

Et changer mon appel « split » pour faire une scission regex sur esperluette qui ne sont pas suivies par d'autres esperluette.

>>> import re 
>>> tmp = "&yI &creally &blove A && W &uRootbeer." 
>>> tmp.split('&') 
['', 'yI ', 'creally ', 'blove A ', '', ' W ', 'uRootbeer.'] 
>>> re.split('MyRegex', tmp) 
['', 'yI ', 'creally ', 'blove A ', '&W ', 'uRootbeer.'] 

Fondamentalement, je besoin d'une expression rationnelle qui va diviser le premier esperluette d'une paire, et chaque unique esperluette, pour me permettre d'échapper via mon dictionnaire.

Si quelqu'un a de meilleures solutions s'il vous plaît n'hésitez pas à me le faire savoir.

+0

Mike, je suis un peu perplexe (mais pas mal personnellement) pourquoi ne pas utiliser ma solution de cette autre question. Il s'est avéré être le plus rapide sur les données réelles, * a * la propriété de garder des esperluettes réelles dans la sortie, et est certainement parmi les plus lisibles des réponses données. –

+0

Peter: La raison en est que je n'avais pas encore lu votre commentaire sur la raison pour laquelle je recevais les erreurs que j'étais et que je n'ai pas trouvé de solution au moment où j'ai eu besoin d'écrire ce code. Maintenant que je vois vos commentaires, il est probable que je vais changer le code pour utiliser votre solution plus rapide et plus lisible. –

Répondre

2

Vous pouvez utiliser un lookbehind négatif (en supposant que le moteur d'expressions rationnelles en question le supporte) pour ne faire correspondre que des esperluettes qui ne suivent pas une autre esperluette.

/(?<!&)&/ 
+0

Cela a fonctionné parfaitement. Je ne sais pas quel genre de sacrifices de vitesse je fais en faisant un lookbehind, donc si quelqu'un peut trouver une solution plus efficace (si elle existe même), je serai heureux de l'entendre. –

+0

Comme indiqué ci-dessus en commentaire à votre question, ma solution est en fait plus rapide que celle de gnibbler avant même que vous la changiez pour utiliser une division regex. Dans tous les cas, j'ai inclus un code de test avec une entrée simulée correcte qui devrait facilement vous permettre de comparer le changement de performance si vous suivez cette approche. –

0

boucle Peut-être alors que (q = str.find ('&', p)) = -1, puis ajoutez la partie gauche (p + 2 q - 1)! Et la valeur de remplacement.

0

Je pense que cela fait l'affaire:

import re 

def fix(text): 
    dict = {"y":"\033[0;30m", 
      "c":"\033[0;31m", 
      "b":"\033[0;32m", 
      "Y":"\033[0;33m", 
      "u":"\033[0;34m", 
      "&":"&"} 

    myparts = re.split('\&(\&*)', text) 
    myparts[1:]=[dict.get(x[0],"&"+x[0])+x[1:] if len(x) > 0 else x for x in myparts[1:]] 
    result = "".join(myparts) 
    return result 


print fix("The &yquick &cbrown &bfox &Yjumps over the &ulazy dog") 
print fix("&yI &creally &blove A && W &uRootbeer.") 
0

re.sub fera ce que vous voulez. Il prend un motif regex et peut prendre une fonction pour traiter le match et retourner le remplacement. Ci-dessous, si le caractère suivant le & n'est pas dans le dictionnaire, aucun remplacement n'est effectué. & & est remplacé par & pour permettre d'échapper un & suivi d'un caractère dans le dictionnaire.

De même, 'str' et 'dict' sont de mauvais noms de variables car ils masquent les fonctions intégrées du même nom.

En dessous, '& cat' 'de ne sera pas affecté et '& & chat' deviendra "& chat" supprimer & traduction c.

import re 

s = "The &yquick &cbrown &bfox & cat &&cat &Yjumps over the &ulazy dog" 

D = {"y":"\033[0;30m", 
    "c":"\033[0;31m", 
    "b":"\033[0;32m", 
    "Y":"\033[0;33m", 
    "u":"\033[0;34m", 
    "&":"&"} 

def func(m): 
    return D.get(m.group(1),m.group(0)) 

print repr(re.sub(r'&(.)',func,s)) 

SORTIE:

'The \x1b[0;30mquick \x1b[0;31mbrown \x1b[0;32mfox & cat &cat \x1b[0;33mjumps over the \x1b[0;34mlazy dog' 

-Mark