Як замінити кілька підрядків рядка?


284

Я хотів би використовувати .replace функцію для заміни декількох рядків.

На даний момент я маю

string.replace("condition1", "")

але хотілося б мати щось подібне

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

хоча це не відчуває себе гарним синтаксисом

який правильний спосіб це зробити? на кшталт того, як у grep / regex ви можете зробити \1та \2замінити поля певним рядкам пошуку


7
Ви спробували всі запропоновані рішення? Який з них швидший?
tommy.carstensen

Я знайшов час, щоб перевірити всі відповіді в різних сценаріях. Дивіться stackoverflow.com/questions/59072514/…
Пабло

1
Чесно кажучи, я віддаю перевагу вашому ланцюговому підходу до всіх інших. Я приземлився тут, шукаючи рішення, і використав ваше, і це працює чудово.
frakman1

@ frakman1 +1. немає поняття, чому цього більше не заважають. Усі інші методи ускладнюють читання способу коду. Якби для заміни був масив пропуску функції, це працювало б. Але ваш прикований метод найбільш зрозумілий (принаймні зі статичною кількістю замін)
IceFire

Відповіді:


269

Ось короткий приклад, який повинен робити трюк з регулярними виразами:

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)

Наприклад:

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

7
Заміна відбувається за один прохід.
Ендрю Кларк

26
dkamins: він не надто розумний, він навіть не такий розумний, як це має бути (ми повинні переробити ключі, перш ніж з'єднати їх із "|"). чому це не переосмислено? тому що таким чином ми робимо це за один прохід (= швидкий), і робимо всі заміни одночасно, уникаючи сутичок, як "spamham sha".replace("spam", "eggs").replace("sha","md5")бути "eggmd5m md5"замість цього"eggsham md5"
літаючий овець

8
@AndrewClark Я дуже вдячний, якби ви могли пояснити, що відбувається на останньому рядку з лямбда.
корисні копалини

11
Привіт, я створив невеличку суть із чіткішою версією цього фрагмента. Це також повинно бути трохи ефективніше: gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729
bgusach

15
Для python 3 використовуйте items () замість iteritems ().
Джангарі

127

Ви можете просто зробити гарну маленьку функцію циклічного циклу.

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

де textповна рядок і dicє словник - кожне визначення - це рядок, який замінить відповідність терміну.

Примітка : у Python 3 iteritems()замінено наitems()


Обережно: словники Python не мають надійного порядку ітерації. Це рішення вирішує вашу проблему лише в тому випадку, якщо:

  • порядок заміни не має значення
  • нормально, щоб заміна змінила результати попередніх замін

Наприклад:

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

Можливий вихід №1:

"Це моя свиня, і це моя свиня".

Можливий вихід №2

"Це моя собака, і це моя свиня".

Одне з можливих виправлень - використовувати OrdersDict.

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)

Вихід:

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

Обережно № 2: Неефективна, якщо ваш textрядок занадто великий або в словнику багато пар.


37
Порядок, в якому ви застосовуєте різні заміни, буде мати значення - тому замість використання стандартного диктанту, подумайте про використання OrderedDict- або списку з 2-х кортезів.
козирок

5
Це робить повторення струни вдвічі ... не добре для виступів.
Валентин Лоренц

6
Виконання продуктивності - це гірше, ніж говорить Валентин, - він буде переходити текст стільки разів, скільки є предметів у діку! Добре, якщо "текст" невеликий, але великий для великого тексту.
JDonner

3
Це хороше рішення для деяких випадків. Наприклад, я просто хочу підписати 2 символи, і мені не байдуже порядок їхнього введення, оскільки клавіші заміни не відповідають жодному значенню. Але я хочу, щоб було зрозуміло, що відбувається.
Натан Гарабедян

5
Зауважте, що це може дати несподівані результати, оскільки нещодавно вставлений текст у першій ітерації може бути узгоджений у другій ітерації. Наприклад, якщо ми наївно намагаємося замінити всі "A" на "B", а всі "B" на "C", рядок "AB" буде перетворений на "CC", а не на "BC".
Ambroz Bizjak

106

Чому б не одне таке рішення?

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
Це дуже корисно, просто і портативно.
Частка

Виглядав приємно, але не замінюючи регулярний вираз, як in: for r in ((r '\ s.', '.'), (R '\ s,', ',')):
Мартін

2
зробити його 1-вкладиш: ss = [s.replace (* r) для r in (("коричневий", "червоний"), ("ледачий", "швидкий")]] [0]
Марк К

95

Ось варіант першого рішення з використанням зменшення, якщо вам подобається функціонувати. :)

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

ще краща версія мартіно:

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

8
Було б простіше скласти replsпослідовність кортежів і покінчити з iteritems()викликом. тобто repls = ('hello', 'goodbye'), ('world', 'earth')і reduce(lambda a, kv: a.replace(*kv), repls, s). Також буде незмінним працювати в Python 3.
martineau

приємно! якщо ви використовуєте python3, використовуйте елементи замість iteritems (тепер видалено в дисках).
e.arbitrio

2
@martineau: Неправда, що це працює в python3, оскільки reduceвін був видалений .
Норманій

5
@normanius: по- reduce, як і раніше існує, однак це було зроблено частина functoolsмодуля (див документації ) в Python 3, тому , коли я сказав без змін, я мав в виду той же код може бути запущений, хоча по загальним визнанням , це вимагало б , що reduceвже був importред при необхідності оскільки це вже не вбудований.
мартіно

35

Це лише більш стислий підсумок чудових відповідей FJ та MiniQuark. Все, що вам потрібно для досягнення кількох одночасних замін рядків, це наступна функція:

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)

Використання:

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

За бажанням ви можете зробити власні спеціальні функції заміни, починаючи з цієї більш простої.


1
Хоча це хороше рішення, паралельна заміна рядків не дасть точно таких же результатів, як їх послідовне (ланцюгове), хоча це може не мати значення.
мартіно

2
Впевнений, rep_dict = {"but": "mut", "mutton": "lamb"}що рядок "button"приводить "mutton"до вашого коду, але дав би, "lamb"якби заміни були ланцюгові одна за одною.
мартіно

2
Це головна особливість цього коду, а не дефект. За допомогою ланцюгових замін він не міг досягти бажаної поведінки заміщення двох слів одночасно та зворотно, як у моєму прикладі.
mmj

1
Це не може здатися чудовою особливістю, якщо вона вам не потрібна. Але тут ми говоримо про одночасні заміни , то це справді головна особливість. З "ланцюговими" замінами результат цього прикладу був би Do you prefer cafe? No, I prefer cafe., що зовсім не бажано.
mmj

@David напишіть власну відповідь, ваша редакція занадто радикальна
UmNyobe

29

Я будував це на чудовій відповіді FJ:

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)

Використання одного знімка:

>>> 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é.

Зауважте, що оскільки заміна виконується всього за один прохід, "кафе" переходить на "чай", але воно не змінюється назад на "кафе".

Якщо вам потрібно зробити одну і ту ж заміну багато разів, ви можете легко створити функцію заміни:

>>> 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!\"

Покращення:

  • перетворив код у функцію
  • додано багатолінійну підтримку
  • виправлено помилку у втечі
  • легко створити функцію для певної багаторазової заміни

Насолоджуйтесь! :-)


1
Чи могла б хтось пояснити цей крок за кроком для піратів-ноубів, як я?
Джуліан Суарес

Співробітник python noob тут, тому я зроблю неповний знімок, щоб зрозуміти це. розділити key_values ​​на речі для заміни (ключі, приєднані "|") та логіку (якщо збіг є ключовою, поверніть значення) b. зробіть синтаксичний аналізатор ("візерунок", який шукає ключі, і використовує задану логіку) - оберніть це у функції лямбда та поверніться. Речі, які я зараз шукаю: re.M, і необхідність лямбда для логіки заміни.
Фокс

1
@Fox Ви його отримали. Ви могли б визначити функцію замість використання лямбда, це просто зробити код коротшим. Але зауважте, що pattern.subочікується, що функція має лише один параметр (текст, який потрібно замінити), тому функція повинна мати доступ до неї replace_dict. re.Mдозволяє заміна Multiline (це добре пояснено в doc: docs.python.org/2/library/re.html#re.M ).
MiniQuark

22

Я хотів би запропонувати використання шаблонів рядків. Просто розмістіть рядок, який потрібно замінити, у словнику і все встановлено! Приклад з 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'

Виглядає добре, але додаючи ключ, який не передбачено, substituteвикликає виняток, тому будьте обережні, отримуючи шаблони від користувачів.
Барт Фрідеріхс

2
Недолік такого підходу полягає в тому, що шаблон повинен містити всі, і не більше всіх, рядків $, які потрібно замінити, дивіться тут
RolfBly

17

У моєму випадку мені потрібна була проста заміна унікальних ключів іменами, тому я придумав це:

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
Це працює до тих пір, поки у вас немає зіткнення щодо заміни. Якщо ви замінили iз sвами б отримати дивну поведінку.
bgusach

1
Якщо порядок значущий, замість диктату вище можна використовувати масив: b = [ ['i', 'Z'], ['s', 'Y'] ]; for x,y in (b): a = a.replace(x, y) Тоді якщо ви обережні, щоб замовити пари масивів, ви можете переконатися, що ви не замінюєте () рекурсивно.
CODE-READ

Здається, що дикти тепер підтримують порядок , з Python 3.7.0. Я перевірив це, і він працює на замовлення на моїй машині з останнім стабільним Python 3.
Джеймс Косс

15

Починаючи Python 3.8та вводячи вирази призначення (PEP 572) ( :=оператор), ми можемо застосувати заміни в межах розуміння списку:

# 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'

Чи знаєте ви, чи це ефективніше, ніж використовувати заміну в циклі? Я перевіряю всі відповіді на ефективність, але у мене поки немає 3,8.
Пабло

Чому я отримую результат у списку?
johnrao07

1
@ johnrao07 Добре розуміння списку створює список. Ось чому в цьому випадку ви отримуєте ['The quick red fox jumps over the lazy dog', 'The quick red fox jumps over the quick dog']. Але вираз присвоєння ( text := text.replace) також ітеративно створює нові версії textмутації. Після розуміння списку, ви можете використовувати textзмінну, яка містить змінений текст.
Ксав'є Гіхот

1
Якщо ви хочете повернути нову версію textяк однолінійний, ви також можете використовувати [text := text.replace(a, b) for a, b in replacements][-1](зверніть увагу [-1]), який витягує останній елемент розуміння списку; тобто остання версія text.
Ксав'є Гіхот

13

Тут мій 0,02 долара. Він заснований на відповіді Ендрю Кларка, лише трохи зрозуміліше, і він також охоплює випадок, коли рядок, яку потрібно замінити, є підрядком іншої рядки, яку потрібно замінити (довга струна виграє)

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)

Саме в цій суті , сміливо змінюйте його, якщо у вас є якісь пропозиції.


1
Натомість це має бути прийнятою відповіддю, тому що регулярний вираз будується з усіх клавіш, сортуючи їх у порядку зменшення довжини та з'єднуючи їх з | оператор чергування регулярних виразів. І сортування необхідне, щоб було обрано найдовший з усіх можливих варіантів, якщо є альтернативи.
Sachin S

Я згоден, що це найкраще рішення, завдяки сортуванням. Крім сортування, тотожне моїй оригінальній відповіді, тому я позичив сортування і для свого рішення, щоб переконатися, що ніхто не пропустить такої важливої ​​функції.
mmj

6

Мені потрібно було рішення, де рядки, які слід замінити, можуть бути звичайними виразами, наприклад, щоб допомогти нормалізувати довгий текст, замінивши кілька символів пробілу на один. Спираючись на ланцюжок відповідей інших, включаючи MiniQuark і mmj, ось що я придумав:

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)

Це працює для прикладів, наведених в інших відповідях, наприклад:

>>> 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.'

Головне для мене, що ви можете також використовувати регулярні вирази, наприклад, замінити лише цілі слова або нормалізувати пробіл:

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

Якщо ви хочете використовувати клавіші словника як звичайні рядки, ви можете уникнути цих, перш ніж викликати multiple_replace, використовуючи, наприклад, цю функцію:

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"

Наступна функція може допомогти у знаходженні помилкових регулярних виразів серед ваших словникових клавіш (оскільки повідомлення про помилку від multiple_replace не дуже вказує):

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

Зауважте, що він не ланцюговує заміни, а виконує їх одночасно. Це робить його більш ефективним, не обмежуючи, що він може зробити. Щоб імітувати ефект ланцюжка, можливо, вам просто потрібно буде додати більше пар заміни рядків і забезпечити очікуване впорядкування пар:

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

Це приємно, дякую. Чи можна вдосконалити, щоб дозволити також використовувати зворотні референції в замінниках? Я не одразу придумав, як це додати.
cmarqu

Відповідь на моє запитання вище stackoverflow.com/questions/45630940 / ...
cmarqu

4

Ось зразок, який є більш ефективним на довгих струнах з багатьма невеликими замінами.

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)

Сенс полягає в тому, щоб уникнути безлічі сполучень довгих струн. Ми подрібнюємо вихідний рядок до фрагментів, замінюючи деякі фрагменти під час формування списку, а потім приєднуємо всю річ назад до рядка.


2

Ви дійсно не повинні робити це так, але я просто вважаю це занадто крутим:

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

Тепер, answerпо черзі , це результат усіх замін

знову ж таки, це дуже хакі, і це не те, що вам слід регулярно користуватися. Але просто приємно знати, що ти можеш зробити щось подібне, якщо коли-небудь буде потрібно.


2

Я також боровся з цією проблемою. З багатьма замінами регулярні вирази борються, і приблизно в чотири рази повільніше, ніж циклічні string.replace(в моїх умовах експерименту).

Вам слід абсолютно спробувати скористатися бібліотекою Flashtext ( повідомлення в блозі тут , Github тут ). У моєму випадку це було трохи більше, ніж на два порядки швидше, від 1,8 с до 0,015 с (звичайні вирази займали 7,7 с) для кожного документа.

Легко знайти приклади використання за посиланнями вище, але це робочий приклад:

    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)

Зауважте, що Flashtext робить заміну за один прохід (щоб уникнути a -> b і b -> c, перекладаючи "a" на "c"). Flashtext також шукає цілі слова (тому "є" не збігається з " це є "). Це добре працює, якщо у вашої цілі кілька слів (замінивши "Це" на "Привіт").


Як це працює, якщо вам потрібно замінити теги HTML? Наприклад, замініть <p>на /n. Я спробував ваш підхід, але теги flashtext, здається, не розбирають його?
псевдонім51

1
Я не впевнений, чому це не працює, як ви очікували. Одна з можливостей полягає в тому, що ці теги не розділені пробілами, і пам’ятайте, що Flashtext шукає цілі слова. Шляхом цього є спочатку скористатися простою заміною, щоб "Привіт <p> там" став "Привіт <p> там". Вам потрібно бути обережним, щоб видалити небажані пробіли, коли ви закінчите (також просту заміну?). Сподіваюся, що це допомагає.
Пабло

Дякую, чи можете ви встановити <та >позначити кінець слова (але бути включеним до заміни)?
alias51

1
Я вважаю, що "слова" позначаються лише пробілами. Можливо, є кілька необов’язкових параметрів, які ви можете встановити в "KeywordProcessor". В іншому випадку розгляньте підхід вище: замініть "<" на "<", застосуйте Flashtext, потім замініть назад (у вашому випадку, наприклад, "<" до "<" і "\ n" до "\ n" може спрацювати).
Пабло

2

Я вважаю, що це питання потребує однорядкової рекурсивної функції лямбда для повноти, просто тому. Так що:

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

Використання:

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

Примітки:

  • Це споживає словник введення.
  • Піктони Python зберігають ключовий порядок станом на 3,6; відповідні застереження в інших відповідях більше не актуальні. Для зворотної сумісності можна вдатися до версії на основі кортежу:
>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.pop()), d)
>>> mrep('abcabc', [('a', '1'), ('c', '2')])

Примітка: Як і у всіх рекурсивних функцій python, занадто велика глибина рекурсії (тобто занадто великі словники заміщення) призведе до помилки. Дивіться, наприклад, тут .


Я наткнувся на RecursionError при використанні великого словника!
Пабло

@Pablo Цікаво. Як великі? Зауважте, що це відбувається для всіх рекурсивних функцій. Дивись, наприклад , тут: stackoverflow.com/questions/3323001 / ...
mcsoini

Мої слова замін близько до 100k термінів ... до сих пір , використовуючи string.replace це , безумовно , кращим підходом.
Пабло

1
@Pablo в цьому випадку ви не можете використовувати рекурсивні функції. Загалом, sys.getrecursionlimit()це пара 1000, макс. скористайтеся циклом чи чимось подібним або спробуйте спростити заміну.
mcsoini

Так, я боюся, що тут дійсно немає ярлика.
Пабло

1

Я не знаю про швидкість, але це мій робочий день швидкого виправлення:

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

... але мені подобається відповідь-регекс №1 вище. Примітка - якщо одне нове значення є підрядком іншого, то операція не є комутативною.


1

Ви можете використовувати pandasбібліотеку та replaceфункцію, яка підтримує як точні збіги, так і заміни регулярного вираження. Наприклад:

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

І змінений текст:

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

Ви можете знайти приклад тут . Зауважте, що заміни на тексті проводяться в тому порядку, який вони відображаються у списках


1

Для заміни лише одного символу використовуйте translateі str.maketransмій улюблений метод.

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


демонстрація

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

Починаючи з дорогоцінної відповіді Ендрю, я розробив сценарій, який завантажує словник з файлу та опрацьовує всі файли у відкритій папці, щоб зробити заміни. Сценарій завантажує відображення із зовнішнього файлу, в якому можна встановити роздільник. Я початківець, але мені здалося, що цей скрипт дуже корисний при здійсненні декількох підстановок у кількох файлах. Він завантажив словник з більш ніж 1000 записами за секунди. Це не елегантно, але це працювало для мене

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

це моє вирішення проблеми. Я використовував це в чаті, щоб замінити відразу різні слова.

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

це стане The cat hunts the dog


0

Інший приклад: Список вводу

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

Бажаний вихід буде

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

Код:

[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

Або просто для швидкого злому:

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

Ось ще один спосіб зробити це зі словником:

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.