Como substituir várias substrings de uma string?


284

Gostaria de usar a função .replace para substituir várias seqüências de caracteres.

Eu tenho atualmente

string.replace("condition1", "")

mas gostaria de ter algo como

string.replace("condition1", "").replace("condition2", "text")

embora isso não pareça uma boa sintaxe

qual é a maneira correta de fazer isso? tipo como no grep / regex você pode fazer \1e \2substituir os campos por certas strings de pesquisa


7
Você tentou todas as soluções fornecidas? Qual é o mais rápido?
tommy.carstensen

Dispensei um tempo para testar todas as respostas em diferentes cenários. Veja stackoverflow.com/questions/59072514/…
Pablo

1
Honestamente, prefiro sua abordagem encadeada a todas as outras. Cheguei aqui enquanto procurava uma solução e usei a sua e ela funciona muito bem.
frakman1

@ frakman1 +1. nenhuma pista por que isso não é mais votado. Todos os outros métodos tornam o código muito mais difícil de ler. Se houvesse uma matriz de passes de função para substituir, isso funcionaria. Mas seu método encadeado é mais claro (pelo menos com um número estático de substituições)
IceFire

Respostas:


269

Aqui está um pequeno exemplo que deve funcionar com expressões regulares:

import re

rep = {"condition1": "", "condition2": "text"} # define desired replacements here

# use these three lines to do the replacement
rep = dict((re.escape(k), v) for k, v in rep.iteritems()) 
#Python 3 renamed dict.iteritems to dict.items so use rep.items() for latest versions
pattern = re.compile("|".join(rep.keys()))
text = pattern.sub(lambda m: rep[re.escape(m.group(0))], text)

Por exemplo:

>>> pattern.sub(lambda m: rep[re.escape(m.group(0))], "(condition1) and --condition2--")
'() and --text--'

7
A substituição acontece em um único passe.
Andrew Clark

26
dkamins: não é muito inteligente, nem tão inteligente quanto deveria ser (devemos regex-escapar das chaves antes de juntá-las a "|"). por que isso não é superengenharia? porque desta forma podemos fazê-lo em uma passagem (= rápido), e fazemos todas as substituições ao mesmo tempo, evitando confrontos como "spamham sha".replace("spam", "eggs").replace("sha","md5")sendo "eggmd5m md5", em vez de"eggsham md5"
voando ovelhas

8
@AndrewClark Eu apreciaria muito se você pudesse explicar o que está acontecendo na última linha com o lambda.
minerals

11
Olá, criei uma pequena essência com uma versão mais clara deste trecho. Também deve ser um pouco mais eficiente: gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729
bgusach

15
Para python 3, use items () em vez de iteritems ().
Jangari 9/09/17

127

Você poderia apenas criar uma pequena função de loop.

def replace_all(text, dic):
    for i, j in dic.iteritems():
        text = text.replace(i, j)
    return text

onde texté a string completa e dicé um dicionário - cada definição é uma string que substituirá uma correspondência com o termo.

Nota : no Python 3, iteritems()foi substituído poritems()


Cuidado: os dicionários Python não têm uma ordem confiável para iteração. Esta solução só resolve o seu problema se:

  • a ordem das substituições é irrelevante
  • não há problema em uma substituição alterar os resultados de substituições anteriores

Por exemplo:

d = { "cat": "dog", "dog": "pig"}
my_sentence = "This is my cat and this is my dog."
replace_all(my_sentence, d)
print(my_sentence)

Saída possível # 1:

"Este é o meu porco e este é o meu porco."

Saída possível # 2

"Este é o meu cachorro e este é o meu porco."

Uma correção possível é usar um OrderedDict.

from collections import OrderedDict
def replace_all(text, dic):
    for i, j in dic.items():
        text = text.replace(i, j)
    return text
od = OrderedDict([("cat", "dog"), ("dog", "pig")])
my_sentence = "This is my cat and this is my dog."
replace_all(my_sentence, od)
print(my_sentence)

Resultado:

"This is my pig and this is my pig."

Cuidado # 2: Ineficiente se sua textcorda for muito grande ou se houver muitos pares no dicionário.


37
A ordem em que você aplica as diferentes substituições será importante - portanto, em vez de usar um ditado padrão, considere usar um OrderedDict- ou uma lista de duas tuplas.
slothrop

5
Isso faz com que iterar a corda duas vezes ... não seja bom para performances.
Valentin Lorentz

6
Em termos de desempenho, é pior do que o que Valentin diz - ele percorre o texto quantas vezes houver itens na dic! Ótimo se 'texto' for pequeno, mas péssimo para texto grande.
JDonner 17/12 /

3
Esta é uma boa solução para alguns casos. Por exemplo, eu apenas quero sub 2 caracteres e não me importo com a ordem em que eles entram, porque as chaves de substituição não correspondem a nenhum valor. Mas quero que fique claro o que está acontecendo.
Nathan Garabedian

5
Observe que isso pode gerar resultados inesperados porque o texto recém-inserido na primeira iteração pode ser correspondido na segunda iteração. Por exemplo, se tentarmos ingenuamente substituir todos os 'A' por 'B' e todos os 'B' por 'C', a cadeia 'AB' será transformada em 'CC' e não em 'BC'.
precisa saber é o seguinte

105

Por que não uma solução como essa?

s = "The quick brown fox jumps over the lazy dog"
for r in (("brown", "red"), ("lazy", "quick")):
    s = s.replace(*r)

#output will be:  The quick red fox jumps over the quick dog

2
Isso é super útil, simples e portátil.
Shred

Parecia bom, mas não substituindo o regex como em: para r em ((r '\ s.', '.'), (R '\ s,', ',')):
Martin

2
para torná-lo 1-liner: ss = [s.replace (* r) for r in (("brown", "red"), ("preguiçoso", "rápido"))] [0]
Mark K

94

Aqui está uma variante da primeira solução usando o método reduzir, caso você queira ser funcional. :)

repls = {'hello' : 'goodbye', 'world' : 'earth'}
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls.iteritems(), s)

a versão ainda melhor de martineau:

repls = ('hello', 'goodbye'), ('world', 'earth')
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls, s)

8
Seria mais simples criar replsuma sequência de tuplas e acabar com a iteritems()chamada. ie repls = ('hello', 'goodbye'), ('world', 'earth')e reduce(lambda a, kv: a.replace(*kv), repls, s). Também funcionaria inalterado no Python 3. #
martineau

legais! se você usa python3, use itens em vez de iteritems (agora removidos no material dict).
e.arbitrio

2
@martineau: Não é verdade que isso funcione inalterado no python3 desde reduceque foi removido .
Normanius

5
@ normanius: reduceainda existe, no entanto, foi feito parte do functoolsmódulo (consulte a documentação ) no Python 3; portanto, quando eu disse inalterado, quis dizer que o mesmo código poderia ser executado - embora seja certo que exigiria que reducefosse importeditado, se necessário já que não é mais um built-in.
martineau

35

Esta é apenas uma recapitulação mais concisa das ótimas respostas de FJ e MiniQuark. Tudo o que você precisa para obter várias substituições simultâneas de strings é a seguinte função:

def multiple_replace(string, rep_dict):
    pattern = re.compile("|".join([re.escape(k) for k in sorted(rep_dict,key=len,reverse=True)]), flags=re.DOTALL)
    return pattern.sub(lambda x: rep_dict[x.group(0)], string)

Uso:

>>>multiple_replace("Do you like cafe? No, I prefer tea.", {'cafe':'tea', 'tea':'cafe', 'like':'prefer'})
'Do you prefer tea? No, I prefer cafe.'

Se desejar, você pode criar suas próprias funções de substituição dedicadas a partir desta mais simples.


1
Embora essa seja uma boa solução, as substituições simultâneas de strings não fornecerão exatamente os mesmos resultados que as executadas seqüencialmente (em encadeamento) -, embora isso não importe.
5133 martineau

2
Claro, com rep_dict = {"but": "mut", "mutton": "lamb"}a string "button"resulta em "mutton"seu código, mas daria "lamb"se as substituições fossem encadeadas, uma após a outra.
martineau

2
Essa é a principal característica desse código, não um defeito. Com substituições encadeadas, não era possível alcançar o comportamento desejado de substituir duas palavras simultaneamente e reciprocamente, como no meu exemplo.
MMJ

1
Pode não parecer um ótimo recurso se você não precisar. Mas aqui estamos falando de substituições simultâneas , então é de fato a principal característica. Com substituições "encadeadas", a saída do exemplo seria Do you prefer cafe? No, I prefer cafe., o que não é desejável.
MMJ

@ David escrever sua própria resposta, a sua edição é demasiado radical
UmNyobe

29

Eu construí isso com base na excelente resposta dos FJs:

import re

def multiple_replacer(*key_values):
    replace_dict = dict(key_values)
    replacement_function = lambda match: replace_dict[match.group(0)]
    pattern = re.compile("|".join([re.escape(k) for k, v in key_values]), re.M)
    return lambda string: pattern.sub(replacement_function, string)

def multiple_replace(string, *key_values):
    return multiple_replacer(*key_values)(string)

Uso de uma tomada:

>>> replacements = (u"café", u"tea"), (u"tea", u"café"), (u"like", u"love")
>>> print multiple_replace(u"Do you like café? No, I prefer tea.", *replacements)
Do you love tea? No, I prefer café.

Observe que, como a substituição é feita em apenas uma passagem, "café" muda para "chá", mas não volta para "café".

Se você precisar fazer a mesma substituição várias vezes, poderá criar uma função de substituição facilmente:

>>> my_escaper = multiple_replacer(('"','\\"'), ('\t', '\\t'))
>>> many_many_strings = (u'This text will be escaped by "my_escaper"',
                       u'Does this work?\tYes it does',
                       u'And can we span\nmultiple lines?\t"Yes\twe\tcan!"')
>>> for line in many_many_strings:
...     print my_escaper(line)
... 
This text will be escaped by \"my_escaper\"
Does this work?\tYes it does
And can we span
multiple lines?\t\"Yes\twe\tcan!\"

Melhorias:

  • transformou o código em uma função
  • suporte multilinha adicionado
  • corrigido um erro ao escapar
  • fácil criar uma função para uma substituição múltipla específica

Aproveitar! :-)


1
Alguém poderia explicar este passo a passo para noobs python como eu?
Julian Suarez

Companheiro python noob aqui, então eu vou fazer uma tentativa incompleta de entender isso .. a. divida key_values ​​em coisas para substituir (chaves unidas por "|") e lógica (se a correspondência for uma chave, retorne valor) b. faça um analisador de expressão regular ("padrão" que procure chaves e use a lógica fornecida) - envolva isso em uma função lambda e retorne. Coisas que estou pesquisando agora: re.M, e a necessidade de lambda para a lógica de substituição.
Fox

1
@ Fox Você entendeu. Você pode definir uma função em vez de usar um lambda, apenas para tornar o código mais curto. Mas observe que pattern.subespera uma função com apenas um parâmetro (o texto a ser substituído), portanto, a função precisa ter acesso replace_dict. re.Mpermite substituições de várias linhas (isso está bem explicado no doc: docs.python.org/2/library/re.html#re.M ).
MiniQuark

22

Eu gostaria de propor o uso de modelos de string. Basta colocar a string a ser substituída em um dicionário e tudo está pronto! Exemplo de docs.python.org

>>> from string import Template
>>> s = Template('$who likes $what')
>>> s.substitute(who='tim', what='kung pao')
'tim likes kung pao'
>>> d = dict(who='tim')
>>> Template('Give $who $100').substitute(d)
Traceback (most recent call last):
[...]
ValueError: Invalid placeholder in string: line 1, col 10
>>> Template('$who likes $what').substitute(d)
Traceback (most recent call last):
[...]
KeyError: 'what'
>>> Template('$who likes $what').safe_substitute(d)
'tim likes $what'

Parece bom, mas ao adicionar uma chave não fornecida substitutegera uma exceção, portanto, tenha cuidado ao obter modelos dos usuários.
Bart Friederichs

2
A desvantagem dessa abordagem é que o modelo deve conter tudo, e não mais do que tudo, $ cordas de ser substituído, ver aqui
RolfBly

17

No meu caso, eu precisava de uma simples substituição de chaves exclusivas por nomes, então pensei nisso:

a = 'This is a test string.'
b = {'i': 'I', 's': 'S'}
for x,y in b.items():
    a = a.replace(x, y)
>>> a
'ThIS IS a teSt StrIng.'

3
Isso funciona desde que você não tenha um conflito de substituição. Se você substituiu icom svocê teria um comportamento estranho.
precisa saber é

1
Se a ordem for significativa, em vez do ditado acima, você poderá usar uma matriz: b = [ ['i', 'Z'], ['s', 'Y'] ]; for x,y in (b): a = a.replace(x, y) Então, se você for cuidadoso ao solicitar seus pares de matrizes, pode garantir que não substitua () recursivamente.
CODE-REaD

Parece que os ditados agora mantêm a ordem , a partir do Python 3.7.0. Eu testei e funciona em ordem na minha máquina com o mais recente estável Python 3.
James Koss

15

Iniciando Python 3.8e introduzindo expressões de atribuição (PEP 572) ( :=operador), podemos aplicar as substituições dentro de uma compreensão de lista:

# text = "The quick brown fox jumps over the lazy dog"
# replacements = [("brown", "red"), ("lazy", "quick")]
[text := text.replace(a, b) for a, b in replacements]
# text = 'The quick red fox jumps over the quick dog'

Você sabe se isso é mais eficiente do que usar substituir em um loop? Estou testando todas as respostas para desempenho, mas ainda não tenho 3,8.
26619 Pablo Pablo

Por que recebo a saída em uma lista?
johnrao07 27/04

1
@ johnrao07 Bem, a compreensão de uma lista cria uma lista. É por isso que, neste caso, você recebe ['The quick red fox jumps over the lazy dog', 'The quick red fox jumps over the quick dog']. Mas a expressão de atribuição ( text := text.replace) também cria iterativamente novas versões text, modificando-a. Após a compreensão da lista, você pode usar a textvariável que contém o texto modificado.
Xavier Guihot 27/04

1
Se você deseja retornar a nova versão textcomo uma linha, também pode usar [text := text.replace(a, b) for a, b in replacements][-1](observe o [-1]), que extrai o último elemento da compreensão da lista; ou seja, a última versão do text.
Xavier Guihot 27/04

13

Aqui meus $ 0,02. Baseia-se na resposta de Andrew Clark, um pouco mais clara, e também abrange o caso em que uma sequência a ser substituída é uma substring de outra sequência a ser substituída (vitórias mais longas)

def multireplace(string, replacements):
    """
    Given a string and a replacement map, it returns the replaced string.

    :param str string: string to execute replacements on
    :param dict replacements: replacement dictionary {value to find: value to replace}
    :rtype: str

    """
    # Place longer ones first to keep shorter substrings from matching
    # where the longer ones should take place
    # For instance given the replacements {'ab': 'AB', 'abc': 'ABC'} against 
    # the string 'hey abc', it should produce 'hey ABC' and not 'hey ABc'
    substrs = sorted(replacements, key=len, reverse=True)

    # Create a big OR regex that matches any of the substrings to replace
    regexp = re.compile('|'.join(map(re.escape, substrs)))

    # For each match, look up the new string in the replacements
    return regexp.sub(lambda match: replacements[match.group(0)], string)

É nessa essência , sinta-se à vontade para modificá-la se você tiver alguma proposta.


1
Essa deveria ter sido a resposta aceita, porque o regex é construído a partir de todas as chaves, classificando-as em ordem decrescente de comprimento e juntando-as à | operador de alternância regex. E a classificação é necessária para que a maior de todas as opções possíveis seja selecionada se houver alguma alternativa.
Sachin S

Concordo que esta é a melhor solução, graças à classificação. Além da classificação, é idêntico à minha resposta original; portanto, emprestei a classificação também para a minha solução, para garantir que ninguém perca um recurso tão importante.
MMJ

6

Eu precisava de uma solução em que as seqüências a serem substituídas pudessem ser expressões regulares, por exemplo, para ajudar a normalizar um texto longo, substituindo vários caracteres de espaço em branco por um único. Com base em uma cadeia de respostas de outras pessoas, incluindo MiniQuark e mmj, é isso que eu criei:

def multiple_replace(string, reps, re_flags = 0):
    """ Transforms string, replacing keys from re_str_dict with values.
    reps: dictionary, or list of key-value pairs (to enforce ordering;
          earlier items have higher priority).
          Keys are used as regular expressions.
    re_flags: interpretation of regular expressions, such as re.DOTALL
    """
    if isinstance(reps, dict):
        reps = reps.items()
    pattern = re.compile("|".join("(?P<_%d>%s)" % (i, re_str[0])
                                  for i, re_str in enumerate(reps)),
                         re_flags)
    return pattern.sub(lambda x: reps[int(x.lastgroup[1:])][1], string)

Ele funciona para os exemplos dados em outras respostas, por exemplo:

>>> multiple_replace("(condition1) and --condition2--",
...                  {"condition1": "", "condition2": "text"})
'() and --text--'

>>> multiple_replace('hello, world', {'hello' : 'goodbye', 'world' : 'earth'})
'goodbye, earth'

>>> multiple_replace("Do you like cafe? No, I prefer tea.",
...                  {'cafe': 'tea', 'tea': 'cafe', 'like': 'prefer'})
'Do you prefer tea? No, I prefer cafe.'

O principal para mim é que você também pode usar expressões regulares, por exemplo, para substituir apenas palavras inteiras ou para normalizar o espaço em branco:

>>> s = "I don't want to change this name:\n  Philip II of Spain"
>>> re_str_dict = {r'\bI\b': 'You', r'[\n\t ]+': ' '}
>>> multiple_replace(s, re_str_dict)
"You don't want to change this name: Philip II of Spain"

Se você quiser usar as teclas de dicionário como seqüências normais, poderá escapar delas antes de chamar multiple_replace usando, por exemplo, esta função:

def escape_keys(d):
    """ transform dictionary d by applying re.escape to the keys """
    return dict((re.escape(k), v) for k, v in d.items())

>>> multiple_replace(s, escape_keys(re_str_dict))
"I don't want to change this name:\n  Philip II of Spain"

A função a seguir pode ajudar a encontrar expressões regulares incorretas entre as chaves do seu dicionário (já que a mensagem de erro de multiple_replace não é muito reveladora):

def check_re_list(re_list):
    """ Checks if each regular expression in list is well-formed. """
    for i, e in enumerate(re_list):
        try:
            re.compile(e)
        except (TypeError, re.error):
            print("Invalid regular expression string "
                  "at position {}: '{}'".format(i, e))

>>> check_re_list(re_str_dict.keys())

Observe que ele não encadeia as substituições, mas as executa simultaneamente. Isso o torna mais eficiente sem restringir o que ele pode fazer. Para imitar o efeito do encadeamento, talvez seja necessário adicionar mais pares de substituição de cadeia e garantir a ordem esperada dos pares:

>>> multiple_replace("button", {"but": "mut", "mutton": "lamb"})
'mutton'
>>> multiple_replace("button", [("button", "lamb"),
...                             ("but", "mut"), ("mutton", "lamb")])
'lamb'

Isso é legal, obrigado. Poderia ser aprimorado para permitir também que as referências anteriores sejam usadas nas substituições? Ainda não descobri como adicionar isso.
Cmarqu 10/08

A resposta à minha pergunta acima é stackoverflow.com/questions/45630940/...
cmarqu

4

Aqui está uma amostra que é mais eficiente em seqüências longas com muitas substituições pequenas.

source = "Here is foo, it does moo!"

replacements = {
    'is': 'was', # replace 'is' with 'was'
    'does': 'did',
    '!': '?'
}

def replace(source, replacements):
    finder = re.compile("|".join(re.escape(k) for k in replacements.keys())) # matches every string we want replaced
    result = []
    pos = 0
    while True:
        match = finder.search(source, pos)
        if match:
            # cut off the part up until match
            result.append(source[pos : match.start()])
            # cut off the matched part and replace it in place
            result.append(replacements[source[match.start() : match.end()]])
            pos = match.end()
        else:
            # the rest after the last match
            result.append(source[pos:])
            break
    return "".join(result)

print replace(source, replacements)

O objetivo é evitar muitas concatenações de seqüências longas. Cortamos a string de origem em fragmentos, substituindo alguns dos fragmentos à medida que formamos a lista e, em seguida, juntamos tudo novamente em uma string.


2

Você realmente não deve fazê-lo dessa maneira, mas acho muito legal:

>>> replacements = {'cond1':'text1', 'cond2':'text2'}
>>> cmd = 'answer = s'
>>> for k,v in replacements.iteritems():
>>>     cmd += ".replace(%s, %s)" %(k,v)
>>> exec(cmd)

Agora, answeré o resultado de todas as substituições, por sua vez

novamente, isso é muito hacky e não é algo que você deveria usar regularmente. Mas é bom saber que você pode fazer algo assim, se precisar.


2

Eu estava lutando com esse problema também. Com muitas substituições, as expressões regulares lutam e são cerca de quatro vezes mais lentas que as repetidas string.replace(nas condições da minha experiência).

Você deve absolutamente tentar usar a biblioteca Flashtext ( postagem no blog aqui , Github aqui ). No meu caso , foi um pouco mais do que duas ordens de magnitude mais rápido, de 1,8 sa 0,015 s (expressões regulares levaram 7,7 s) para cada documento.

É fácil encontrar exemplos de uso nos links acima, mas este é um exemplo de trabalho:

    from flashtext import KeywordProcessor
    self.processor = KeywordProcessor(case_sensitive=False)
    for k, v in self.my_dict.items():
        self.processor.add_keyword(k, v)
    new_string = self.processor.replace_keywords(string)

Note-se que Flashtext faz substituições numa única passagem (para evitar a -> b e b -> c traduzindo 'a' em 'c'). O texto em Flash também procura palavras inteiras (portanto, 'is' não corresponderá a 'th is '). Funciona bem se o seu objetivo for várias palavras (substituindo 'Isto é' por 'Olá').


Como isso funciona se você precisar substituir as tags HTML? Por exemplo, substitua <p>por /n. Eu tentei sua abordagem, mas com tags o flashtext não parece analisá-lo?
alias51 15/03

1
Não sei por que não está funcionando como o esperado. Uma possibilidade é que essas tags não sejam separadas por espaços e lembre-se de que o Flashtext procura palavras inteiras. Uma maneira de contornar isso é usar uma substituição simples primeiro, para que "Olá <p> lá" se torne "Olá <p> lá". Você precisaria ter cuidado para remover espaços indesejados quando terminar (também é simples substituir?). Espero que ajude.
Pablo

Obrigado, você pode definir <e >marcar o final de uma palavra (mas ser incluído na substituição)?
alias51 15/03

1
Eu acredito que as "palavras" são marcadas apenas por espaços. Talvez haja alguns parâmetros opcionais que você pode definir em "KeywordProcessor". Caso contrário, considere a abordagem acima: substitua "<" por "<", aplique o Flashtext e substitua novamente (no seu caso, por exemplo, "<" para "<" e "\ n" para "\ n" podem funcionar).
Pablo

2

Eu sinto que esta pergunta precisa de uma resposta lambda recursiva de linha única para completar, apenas porque. Então aí:

>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.popitem()), d)

Uso:

>>> mrep('abcabc', {'a': '1', 'c': '2'})
'1b21b2'

Notas:

  • Isso consome o dicionário de entrada.
  • Os dicionários Python preservam a ordem das chaves a partir de 3,6; advertências correspondentes em outras respostas não são mais relevantes. Para compatibilidade com versões anteriores, pode-se recorrer a uma versão baseada em tupla:
>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.pop()), d)
>>> mrep('abcabc', [('a', '1'), ('c', '2')])

Nota: Como em todas as funções recursivas do python, uma profundidade de recursão muito grande (ou seja, dicionários de substituição muito grandes) resultará em um erro. Veja, por exemplo, aqui .


Eu corro em RecursionError ao usar um dicionário grande!
26619 Pablo Pablo

@Pablo Interessante. Quão largo? Observe que isso acontece para todas as funções recursivas. Veja por exemplo aqui: stackoverflow.com/questions/3323001/…
mcsoini 27/11/19

Meu dicionário de substituições tem quase 100k de termos ... até agora, usando string.replace é de longe a melhor abordagem.
Pablo

1
@ Pablo, nesse caso, você não pode usar funções recursivas. Em geral, sys.getrecursionlimit()é um par de 1000, máx. use um loop ou algo parecido ou tente simplificar as substituições.
Mcsoini 27/11/19

Sim, receio que não haja realmente nenhum atalho aqui.
Pablo

1

Não sei sobre velocidade, mas esta é a minha solução rápida para o dia-a-dia:

reduce(lambda a, b: a.replace(*b)
    , [('o','W'), ('t','X')] #iterable of pairs: (oldval, newval)
    , 'tomato' #The string from which to replace values
    )

... mas eu gosto da resposta regex nº 1 acima. Nota - se um novo valor for uma substring de outro, a operação não será comutativa.


1

Você pode usar a pandasbiblioteca e a replacefunção que suporta correspondências exatas e substituições de regex. Por exemplo:

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

E o texto modificado é:

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

Você pode encontrar um exemplo aqui . Observe que as substituições no texto são feitas com a ordem em que aparecem nas listas


1

Para substituir apenas um caractere, use o método translatee str.maketransé o meu favorito.

tl; dr> result_string = your_string.translate(str.maketrans(dict_mapping))


demonstração

my_string = 'This is a test string.'
dict_mapping = {'i': 's', 's': 'S'}
result_good = my_string.translate(str.maketrans(dict_mapping))
result_bad = my_string
for x, y in dict_mapping.items():
    result_bad = result_bad.replace(x, y)
print(result_good)  # ThsS sS a teSt Strsng.
print(result_bad)   # ThSS SS a teSt StrSng.

0

A partir da resposta preciosa de Andrew, desenvolvi um script que carrega o dicionário de um arquivo e elabora todos os arquivos da pasta aberta para fazer as substituições. O script carrega os mapeamentos de um arquivo externo no qual você pode definir o separador. Sou iniciante, mas achei esse script muito útil ao fazer várias substituições em vários arquivos. Carregou um dicionário com mais de 1000 entradas em segundos. Não é elegante, mas funcionou para mim

import glob
import re

mapfile = input("Enter map file name with extension eg. codifica.txt: ")
sep = input("Enter map file column separator eg. |: ")
mask = input("Enter search mask with extension eg. 2010*txt for all files to be processed: ")
suff = input("Enter suffix with extension eg. _NEW.txt for newly generated files: ")

rep = {} # creation of empy dictionary

with open(mapfile) as temprep: # loading of definitions in the dictionary using input file, separator is prompted
    for line in temprep:
        (key, val) = line.strip('\n').split(sep)
        rep[key] = val

for filename in glob.iglob(mask): # recursion on all the files with the mask prompted

    with open (filename, "r") as textfile: # load each file in the variable text
        text = textfile.read()

        # start replacement
        #rep = dict((re.escape(k), v) for k, v in rep.items()) commented to enable the use in the mapping of re reserved characters
        pattern = re.compile("|".join(rep.keys()))
        text = pattern.sub(lambda m: rep[m.group(0)], text)

        #write of te output files with the prompted suffice
        target = open(filename[:-4]+"_NEW.txt", "w")
        target.write(text)
        target.close()

0

esta é a minha solução para o problema. Usei-o em um chatbot para substituir as diferentes palavras de uma só vez.

def mass_replace(text, dct):
    new_string = ""
    old_string = text
    while len(old_string) > 0:
        s = ""
        sk = ""
        for k in dct.keys():
            if old_string.startswith(k):
                s = dct[k]
                sk = k
        if s:
            new_string+=s
            old_string = old_string[len(sk):]
        else:
            new_string+=old_string[0]
            old_string = old_string[1:]
    return new_string

print mass_replace("The dog hunts the cat", {"dog":"cat", "cat":"dog"})

isso vai se tornar The cat hunts the dog


0

Outro exemplo: lista de entrada

error_list = ['[br]', '[ex]', 'Something']
words = ['how', 'much[ex]', 'is[br]', 'the', 'fish[br]', 'noSomething', 'really']

A saída desejada seria

words = ['how', 'much', 'is', 'the', 'fish', 'no', 'really']

Código:

[n[0][0] if len(n[0]) else n[1] for n in [[[w.replace(e,"") for e in error_list if e in w],w] for w in words]] 

-2

Ou apenas para um hack rápido:

for line in to_read:
    read_buffer = line              
    stripped_buffer1 = read_buffer.replace("term1", " ")
    stripped_buffer2 = stripped_buffer1.replace("term2", " ")
    write_to_file = to_write.write(stripped_buffer2)

-2

Aqui está outra maneira de fazer isso com um dicionário:

listA="The cat jumped over the house".split()
modify = {word:word for number,word in enumerate(listA)}
modify["cat"],modify["jumped"]="dog","walked"
print " ".join(modify[x] for x in listA)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.