web-dev-qa-db-fra.com

Comment puis-je faire plusieurs substitutions en utilisant regex en python?

Je peux utiliser ce code ci-dessous pour créer un nouveau fichier avec la substitution de a avec aa en utilisant des expressions régulières.

import re

with open("notes.txt") as text:
    new_text = re.sub("a", "aa", text.read())
    with open("notes2.txt", "w") as result:
        result.write(new_text)

Je me demandais si je devais utiliser cette ligne, new_text = re.sub("a", "aa", text.read()), plusieurs fois mais substituer la chaîne aux autres lettres que je veux changer pour changer plus d'une lettre dans mon texte?

Autrement dit, donc a--> aa, b--> bb et c--> cc.

Je dois donc écrire cette ligne pour toutes les lettres que je souhaite modifier ou existe-t-il un moyen plus simple. Peut-être pour créer un "dictionnaire" de traductions. Dois-je mettre ces lettres dans un tableau? Je ne sais pas comment faire appel à eux si je le fais.

29
Euridice01

La réponse proposée par @nhahtdh est valide, mais je dirais moins Pythonic que l'exemple canonique, qui utilise du code moins opaque que ses manipulations regex et tire parti des structures de données intégrées de python et de la fonction anonyme.

Un dictionnaire de traductions est logique dans ce contexte. En fait, c'est ainsi que le Python Cookbook le fait, comme indiqué dans cet exemple (copié depuis ActiveState http://code.activestate.com/recipes/81330-single-pass) -multiple-replace / )

import re 

def multiple_replace(dict, text):
  # Create a regular expression  from the dictionary keys
  regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))

  # For each match, look-up corresponding value in dictionary
  return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text) 

if __name__ == "__main__": 

  text = "Larry Wall is the creator of Perl"

  dict = {
    "Larry Wall" : "Guido van Rossum",
    "creator" : "Benevolent Dictator for Life",
    "Perl" : "Python",
  } 

  print multiple_replace(dict, text)

Donc, dans votre cas, vous pourriez faire un dict trans = {"a": "aa", "b": "bb"} puis passez-le dans multiple_replace avec le texte que vous souhaitez traduire. Fondamentalement, cette fonction ne fait que créer une énorme expression régulière contenant toutes vos expressions régulières à traduire, puis lorsqu'une est trouvée, en passant une fonction lambda à regex.sub pour effectuer la recherche dans le dictionnaire de traduction.

Vous pouvez utiliser cette fonction lors de la lecture de votre fichier, par exemple:

with open("notes.txt") as text:
    new_text = multiple_replace(replacements, text.read())
with open("notes2.txt", "w") as result:
    result.write(new_text)

J'ai effectivement utilisé cette méthode exacte dans la production, dans un cas où j'avais besoin de traduire les mois de l'année du tchèque en anglais pour une tâche de raclage Web.

Comme l'a souligné @nhahtdh, un inconvénient de cette approche est qu'elle n'est pas exempte de préfixe: les clés de dictionnaire qui sont des préfixes d'autres clés de dictionnaire entraîneront la rupture de la méthode.

40
Emmett Butler

Vous pouvez utiliser le groupe de capture et la référence arrière:

re.sub(r"([characters])", r"\1\1", text.read())

Mettez les caractères que vous souhaitez doubler entre []. Pour les minuscules a, b, c:

re.sub(r"([abc])", r"\1\1", text.read())

Dans la chaîne de remplacement, vous pouvez faire référence à tout ce qui correspond à un groupe de capture () avec \n notation où n est un positif entier (0 exclu). \1 fait référence au premier groupe de capture. Il y a une autre notation \g<n>n peut être n'importe quel entier non négatif (0 autorisé); \g<0> fera référence à tout le texte correspondant à l'expression.


Si vous souhaitez doubler tous les caractères sauf la nouvelle ligne:

re.sub(r"(.)", r"\1\1", text.read())

Si vous souhaitez doubler tous les caractères (nouvelle ligne incluse):

re.sub(r"(.)", r"\1\1", text.read(), 0, re.S)
16
nhahtdh

En utilisant des astuces de comment créer une classe 'stringy' , nous pouvons créer un objet identique à une chaîne mais pour une méthode supplémentaire sub:

import re
class Substitutable(str):
  def __new__(cls, *args, **kwargs):
    newobj = str.__new__(cls, *args, **kwargs)
    newobj.sub = lambda fro,to: Substitutable(re.sub(fro, to, newobj))
    return newobj

Cela permet d'utiliser le modèle de générateur, qui semble plus joli, mais ne fonctionne que pour un nombre prédéterminé de substitutions. Si vous l'utilisez en boucle, il ne sert plus à rien de créer une classe supplémentaire. Par exemple.

>>> h = Substitutable('horse')
>>> h
'horse'
>>> h.sub('h', 'f')
'forse'
>>> h.sub('h', 'f').sub('f','h')
'horse'
3
Leo

Vous pouvez utiliser la bibliothèque pandas et la fonction replace. Je représente un exemple avec cinq remplacements:

df = pd.DataFrame({'text': ['Billy is going to visit Rome in November', 'I was born in 10/10/2010', 'I will be there at 20:00']})

to_replace=['Billy','Rome','January|February|March|April|May|June|July|August|September|October|November|December', '\d{2}:\d{2}', '\d{2}/\d{2}/\d{4}']
replace_with=['name','city','month','time', 'date']

print(df.text.replace(to_replace, replace_with, regex=True))

Et le texte modifié est:

0    name is going to visit city in month
1                      I was born in date
2                 I will be there at time

Vous pouvez trouver l'exemple ici

1
George Pipis

J'ai découvert que je devais modifier le code d'Emmett J. Butler en changeant la fonction lambda pour utiliser myDict.get (mo.group (1), mo.group (1)). Le code d'origine ne fonctionnait pas pour moi; l'utilisation de myDict.get () offre également l'avantage d'une valeur par défaut si aucune clé n'est trouvée.

OIDNameContraction = {
                                'Fucntion':'Func',
                                'operated':'Operated',
                                'Asist':'Assist',
                                'Detection':'Det',
                                'Control':'Ctrl',
                                'Function':'Func'
}

replacementDictRegex = re.compile("(%s)" % "|".join(map(re.escape, OIDNameContraction.keys())))

oidDescriptionStr = replacementDictRegex.sub(lambda mo:OIDNameContraction.get(mo.group(1),mo.group(1)), oidDescriptionStr)
0
Jordan McBain