2013-03-02 15 views
20

Posso usare questo codice qui sotto per creare un nuovo file con la sostituzione di a con aa usando le espressioni regolari.Come posso fare più sostituzioni usando regex in python?

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) 

Mi chiedevo devo utilizzare questa linea, new_text = re.sub("a", "aa", text.read()), più volte, ma sostituire la stringa per altre lettere che voglio cambiare per cambiare più di una lettera nel mio testo?

Cioè, così a ->aa, b ->bb e c ->cc.

Quindi devo scrivere quella riga per tutte le lettere che voglio cambiare o c'è un modo più semplice. Forse per creare un "dizionario" di traduzioni. Devo mettere quelle lettere in un array? Non sono sicuro di come chiamarli se lo faccio.

risposta

27

La risposta proposta da @nhahtdh è valida, ma direi che è meno pitone di un esempio canonico, che usa un codice meno opaco rispetto alla sua regex manipolazioni e sfrutta le strutture di dati e la funzione anonima incorporate di python.

Un dizionario di traduzioni ha senso in questo contesto. In effetti, è così che il Python Cookbook lo fa, come mostrato in questo esempio (copiato da 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) 

Quindi nel tuo caso, si potrebbe fare un dict trans = {"a": "aa", "b": "bb"} e poi passarlo in multiple_replace insieme al testo vuoi tradotto. Fondamentalmente tutto ciò che sta facendo è la creazione di un'enorme regex contenente tutte le espressioni regolari da tradurre, quindi quando ne viene trovata una, passa una funzione lambda a regex.sub per eseguire la ricerca del dizionario di traduzione.

È possibile utilizzare questa funzione durante la lettura dal file, ad esempio:

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

realtà ho usato questo metodo esatto in produzione, in un caso in cui avevo bisogno di tradurre i mesi dell'anno da Ceco in inglese per un'attività di scraping web.

Come @nhahtdh sottolineato, uno svantaggio di questo approccio è che non è prefisso: Avviso e-chiavi del dizionario che sono prefissi di altri tasti del dizionario farà sì che il metodo di rottura.

+0

Wow, grazie, è più o meno quello che stavo cercando. Ho ancora una domanda di base, come posso ignorare le lettere maiuscole? Quindi se avessi A e volessi anche tradurlo in aa senza aggiungerlo al dizionario. – Euridice01

+0

@ Euridice01: se si desidera ignorare il caso, specificare il flag 're.I' in' re.compile'. – nhahtdh

+0

La tua soluzione attuale non è ancora configurata per il caso d'uso in cui esiste una coppia di parole, una delle quali è il prefisso dell'altra. L'ordine di apparizione nella materia di alternanza. Penso che almeno dovresti affermare questa ipotesi. – nhahtdh

12

È possibile utilizzare gruppo e backreference cattura:

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

caratteri Put che si desidera raddoppiare tra []. Per il caso di minuscolo a, b, c:

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

Nella stringa di sostituzione, è possibile fare riferimento a tutto ciò accompagnato da un gruppo di cattura () con \n notazione dove n è una certa positivo intero (0 escluso) . \1 si riferisce al primo gruppo di acquisizione. C'è un'altra notazione \g<n> dove n può essere un numero intero non negativo (0 consentito); \g<0> farà riferimento all'intero testo corrispondente all'espressione.


Se si vuole raddoppiare tutti i caratteri tranne nuova linea:

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

Se si vuole raddoppiare tutti i caratteri (nuova linea inclusi):

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

Usando le punte da how to make a 'stringy' class, possiamo fare un oggetto identico a una stringa, ma per un extra sub metodo:

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 

Questo permette di utilizzare il builder, che sembra più bello, ma funziona solo per un pre -determinato numero di sostituzioni. Se lo usi in un ciclo, non ha più senso creare una classe extra. Per esempio.

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

ho trovato ho dovuto modificare il codice di Emmett J. Butler cambiando la funzione lambda utilizzare myDict.get (mo.group (1), mo.group (1)). Il codice originale non funzionava per me; l'uso di myDict.get() fornisce anche il vantaggio di un valore predefinito se non viene trovata una chiave.

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) 
Problemi correlati