Bagaimana cara mengganti banyak substring dari suatu string?

284

Saya ingin menggunakan fungsi .replace untuk mengganti beberapa string.

Saat ini saya punya

string.replace("condition1", "")

tetapi ingin memiliki sesuatu seperti

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

meskipun itu tidak terasa seperti sintaks yang baik

apa cara yang tepat untuk melakukan ini? seperti bagaimana di grep / regex yang dapat Anda lakukan \1dan \2untuk mengganti bidang ke string pencarian tertentu

CQM
sumber
7
Apakah Anda mencoba semua solusi yang disediakan? Yang mana yang lebih cepat?
tommy.carstensen
Saya telah mengambil waktu untuk menguji semua jawaban dalam berbagai skenario. Lihat stackoverflow.com/questions/59072514/...
Pablo
1
Jujur, saya lebih suka pendekatan dirantai Anda ke semua yang lain. Saya mendarat di sini sambil mencari solusi dan menggunakan milik Anda dan itu berfungsi dengan baik.
frakman1
@ frakman1 +1. tidak ada petunjuk mengapa ini tidak terunggulkan lagi. Semua metode lain membuat kode lebih sulit dibaca. Jika ada fungsi pass array untuk diganti, ini akan berfungsi. Tetapi metode berantai Anda paling jelas (setidaknya dengan jumlah pengganti statis)
IceFire

Jawaban:

269

Berikut adalah contoh singkat yang harus dilakukan dengan ekspresi reguler:

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)

Sebagai contoh:

>>> pattern.sub(lambda m: rep[re.escape(m.group(0))], "(condition1) and --condition2--")
'() and --text--'
Andrew Clark
sumber
7
Penggantian terjadi dalam sekali operan.
Andrew Clark
26
dkamins: tidak terlalu pintar, bahkan tidak sepintar seharusnya (kita harus regex-escape kunci sebelum bergabung dengan mereka dengan "|"). mengapa itu tidak terlalu direkayasa? karena dengan cara ini kami melakukannya dalam satu pass (= cepat), dan kami melakukan semua penggantian pada saat yang sama, menghindari bentrokan seperti "spamham sha".replace("spam", "eggs").replace("sha","md5")menjadi "eggmd5m md5"bukan"eggsham md5"
terbang domba
8
@AndrewClark Saya akan sangat menghargai jika Anda bisa menjelaskan apa yang terjadi pada baris terakhir dengan lambda.
mineral
11
Hai, saya membuat intisari kecil dengan versi yang lebih jelas dari cuplikan ini. Seharusnya juga sedikit lebih efisien: gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729
bgusach
15
Untuk python 3, gunakan item () alih-alih iteritems ().
Jangari
127

Anda bisa membuat fungsi looping kecil yang menyenangkan.

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

di mana textstring lengkap dan dickamus - setiap definisi adalah string yang akan menggantikan kecocokan dengan istilah tersebut.

Catatan : dalam Python 3, iteritems()telah diganti denganitems()


Hati-hati: kamus Python tidak memiliki urutan yang dapat diandalkan untuk iterasi. Solusi ini hanya menyelesaikan masalah Anda jika:

  • urutan penggantian tidak relevan
  • tidak apa-apa untuk penggantian untuk mengubah hasil penggantian sebelumnya

Misalnya:

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

Output yang mungkin # 1:

"Ini babi saya dan ini babi saya."

Output yang mungkin # 2

"Ini anjing saya dan ini babi saya."

Salah satu perbaikan yang mungkin adalah dengan menggunakan 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)

Keluaran:

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

Hati-hati # 2: Tidak efisien jika textstring Anda terlalu besar atau ada banyak pasangan di kamus.

Joseph Hansen
sumber
37
Urutan di mana Anda menerapkan penggantian yang berbeda akan menjadi masalah - jadi alih-alih menggunakan dict standar, pertimbangkan untuk menggunakan OrderedDict- atau daftar 2-tupel.
slothrop
5
Ini membuat pengulangan string dua kali ... tidak baik untuk pertunjukan.
Valentin Lorentz
6
Dari segi kinerja, ini lebih buruk daripada yang dikatakan Valentin - itu akan melintasi teks sebanyak yang ada dalam item! Baik jika 'teks' kecil tapi, mengerikan untuk teks besar.
JDonner
3
Ini adalah solusi yang bagus untuk beberapa kasus. Sebagai contoh, saya hanya ingin sub 2 karakter dan saya tidak peduli dengan urutan mereka masuk karena kunci substitusi tidak cocok dengan nilai apa pun. Tapi saya ingin memperjelas apa yang terjadi.
Nathan Garabedian
5
Perhatikan bahwa ini dapat memberikan hasil yang tidak terduga karena teks yang baru disisipkan di iterasi pertama dapat dicocokkan dengan iterasi kedua. Misalnya, jika kita secara naif mencoba mengganti semua 'A' dengan 'B' dan semua 'B' dengan 'C', string 'AB' akan diubah menjadi 'CC', dan bukan 'BC'.
Ambroz Bizjak
106

Kenapa tidak ada satu solusi seperti ini?

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
Enrico Bianchi
sumber
2
Ini sangat berguna, sederhana dan portabel.
Rusak
Tampak bagus, tetapi tidak menggantikan regex seperti di: untuk r in ((r '\.', '.'), (R '\ s,', ',')):
Martin
2
untuk membuatnya 1-liner: ss = [s.replace (* r) untuk r in (("brown", "red"), ("lazy", "quick"))] [0]
Mark K
95

Berikut adalah varian dari solusi pertama menggunakan pengurangan, jika Anda suka menjadi fungsional. :)

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

Martineau versi yang lebih baik:

repls = ('hello', 'goodbye'), ('world', 'earth')
s = 'hello, world'
reduce(lambda a, kv: a.replace(*kv), repls, s)
Björn Lindqvist
sumber
8
Akan lebih mudah untuk membuat replsurutan tupel dan melakukan iteritems()panggilan. yaitu repls = ('hello', 'goodbye'), ('world', 'earth')dan reduce(lambda a, kv: a.replace(*kv), repls, s). Akan juga bekerja tidak berubah dalam Python 3.
martineau
bagus! jika Anda menggunakan item penggunaan python3 bukan iteritem (sekarang dihapus dalam hal-hal dikts).
e.arbitrio
2
@martineau: Tidak benar bahwa ini berfungsi tidak berubah di python3 sejak reducedihapus .
normanius
5
@normanius: reducemasih ada, namun itu dibuat menjadi bagian dari functoolsmodul (lihat dokumen ) di Python 3, jadi ketika saya mengatakan tidak berubah, saya maksudkan kode yang sama dapat dijalankan — walaupun diakui itu akan mengharuskan yang reducetelah importdiedit jika perlu karena tidak lagi built-in.
martineau
35

Ini hanyalah rekap singkat dari jawaban bagus FJ dan MiniQuark. Yang Anda butuhkan untuk mencapai beberapa penggantian string simultan adalah fungsi berikut:

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)

Pemakaian:

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

Jika mau, Anda dapat membuat fungsi pengganti khusus Anda sendiri mulai dari yang lebih sederhana ini.

mmj
sumber
1
Meskipun ini adalah solusi yang baik, penggantian string secara bersamaan tidak akan memberikan hasil yang sama persis dengan melakukan mereka secara berurutan (merantai) - meskipun itu mungkin tidak masalah.
martineau
2
Tentu, dengan hasil rep_dict = {"but": "mut", "mutton": "lamb"}string dengan kode Anda, tetapi akan memberikan jika penggantian dirantai, satu demi satu. "button""mutton""lamb"
martineau
2
Itu adalah fitur utama dari kode ini, bukan cacat. Dengan penggantian berantai itu tidak dapat mencapai perilaku yang diinginkan dari mengganti dua kata secara bersamaan dan secara timbal balik seperti dalam contoh saya.
mmj
1
Tampaknya itu bukan fitur yang hebat jika Anda tidak membutuhkannya. Tapi di sini kita berbicara tentang penggantian simultan , maka itu memang fitur utama. Dengan penggantian "dirantai", output dari contoh akan menjadi Do you prefer cafe? No, I prefer cafe., yang tidak diinginkan sama sekali.
mmj
@ David menulis jawaban Anda sendiri, hasil edit Anda terlalu radikal
UmNyobe
29

Saya membangun ini berdasarkan jawaban luar biasa 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)

Penggunaan satu tembakan:

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

Perhatikan bahwa karena penggantian dilakukan hanya dalam satu pass, "café" berubah menjadi "tea", tetapi itu tidak berubah kembali menjadi "café".

Jika Anda perlu melakukan penggantian yang sama berkali-kali, Anda dapat membuat fungsi penggantian dengan mudah:

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

Perbaikan:

  • mengubah kode menjadi fungsi
  • menambahkan dukungan multiline
  • memperbaiki bug saat melarikan diri
  • mudah untuk membuat fungsi untuk beberapa penggantian tertentu

Nikmati! :-)

MiniQuark
sumber
1
Bisakah seseorang menjelaskan langkah demi langkah untuk noo python seperti saya?
Julian Suarez
Rekan python noob di sini, jadi saya akan mengambil bidikan tidak lengkap untuk memahaminya .. a. memecah key_values ​​menjadi barang-untuk-ganti (kunci bergabung dengan "|") dan logika (jika kecocokan adalah kunci, kembalikan nilai) b. membuat parser regex ("pola" yang mencari kunci, dan menggunakan logika yang diberikan) - bungkus ini dalam fungsi lambda dan kembali. Hal-hal yang saya cari sekarang: re.M, dan perlunya lambda untuk logika pengganti.
Fox
1
@Fox Anda mengerti. Anda bisa mendefinisikan fungsi alih-alih menggunakan lambda, itu hanya untuk membuat kode lebih pendek. Tetapi perhatikan bahwa pattern.submengharapkan fungsi hanya dengan satu parameter (teks untuk menggantikan), sehingga fungsi harus memiliki akses ke replace_dict. re.Mmemungkinkan penggantian Multiline (dijelaskan dengan baik dalam dokumen: docs.python.org/2/library/re.html#re.M ).
MiniQuark
22

Saya ingin mengusulkan penggunaan template string. Cukup tempatkan string yang akan diganti dalam kamus dan semua sudah diatur! Contoh dari 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'
Fredrik Pihl
sumber
Terlihat bagus, tetapi saat menambahkan kunci yang tidak disediakan substitutememunculkan pengecualian, jadi berhati-hatilah saat mendapatkan templat dari pengguna.
Bart Friederichs
2
Kelemahan dari pendekatan ini adalah bahwa template harus berisi semua, dan tidak lebih dari semua, $ string yang harus diganti, lihat di sini
RolfBly
17

Dalam kasus saya, saya perlu mengganti kunci unik dengan nama, jadi saya memikirkannya:

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.'
James Koss
sumber
3
Ini berfungsi selama Anda tidak memiliki bentrokan pengganti. Jika Anda diganti idengan sAnda akan mendapatkan perilaku aneh.
bgusach
1
Jika pesanan signifikan, alih-alih dict di atas Anda dapat menggunakan array: b = [ ['i', 'Z'], ['s', 'Y'] ]; for x,y in (b): a = a.replace(x, y) Kemudian jika Anda berhati-hati untuk memesan pasangan array Anda, Anda dapat memastikan Anda tidak mengganti () secara rekursif.
CODE-REaD
Tampaknya dikt sekarang mempertahankan ketertiban , dari Python 3.7.0. Saya mengujinya dan bekerja dengan baik pada mesin saya dengan stabil terbaru Python 3.
James Koss
15

Mulai Python 3.8, dan pengenalan ekspresi penugasan (PEP 572) ( :=operator), kami dapat menerapkan penggantian dalam pemahaman daftar:

# 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'
Xavier Guihot
sumber
Apakah Anda tahu jika ini lebih efisien daripada menggunakan ganti dalam satu lingkaran? Saya menguji semua jawaban untuk kinerja tetapi saya belum memiliki 3,8.
Pablo
Mengapa saya mendapatkan output dalam daftar?
johnrao07
1
@ johnrao07 Pemahaman daftar membangun daftar. Itu sebabnya, dalam hal ini, Anda dapatkan ['The quick red fox jumps over the lazy dog', 'The quick red fox jumps over the quick dog']. Tetapi ekspresi penugasan ( text := text.replace) juga secara iteratif membangun versi baru textdengan memutasikannya. Setelah pemahaman daftar, Anda bisa menggunakan textvariabel yang berisi teks yang dimodifikasi.
Xavier Guihot
1
Jika Anda ingin mengembalikan versi baru textsebagai satu-liner, Anda juga dapat menggunakan [text := text.replace(a, b) for a, b in replacements][-1](perhatikan [-1]), yang mengekstrak elemen terakhir dari pemahaman daftar; yaitu versi terakhir dari text.
Xavier Guihot
13

Di sini $ 0,02 saya. Ini didasarkan pada jawaban Andrew Clark, hanya sedikit lebih jelas, dan itu juga mencakup kasus ketika string untuk menggantikan adalah substring dari string lain untuk mengganti (string yang lebih lama menang)

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)

Dalam inti ini , jangan ragu untuk memodifikasinya jika Anda memiliki proposal.

bgusach
sumber
1
Ini seharusnya menjadi jawaban yang diterima sebagai gantinya karena regex dibangun dari semua kunci dengan mengurutkannya dalam urutan panjang dan bergabung dengan mereka dengan | operator pergantian regex. Dan penyortiran ini diperlukan agar pilihan terpanjang dari semua pilihan yang mungkin dipilih jika ada alternatif.
Sachin S
Saya setuju bahwa ini adalah solusi terbaik, berkat penyortirannya. Terlepas dari pengurutan identik dengan jawaban asli saya, jadi saya meminjam pengurutan untuk solusi saya juga, untuk memastikan tidak ada yang akan kehilangan fitur penting.
mmj
6

Saya membutuhkan solusi di mana string yang akan diganti dapat berupa ekspresi reguler, misalnya untuk membantu dalam menormalkan teks yang panjang dengan mengganti beberapa karakter spasi putih dengan yang tunggal. Membangun rangkaian jawaban dari orang lain, termasuk MiniQuark dan mmj, inilah yang saya temukan:

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)

Ini berfungsi untuk contoh yang diberikan dalam jawaban lain, misalnya:

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

Hal utama bagi saya adalah Anda dapat menggunakan ekspresi reguler juga, misalnya untuk mengganti seluruh kata saja, atau untuk menormalkan ruang putih:

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

Jika Anda ingin menggunakan kunci kamus sebagai string normal, Anda dapat menghindarinya sebelum memanggil multiple_replace menggunakan mis. Fungsi ini:

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"

Fungsi berikut dapat membantu menemukan ekspresi reguler yang salah di antara kunci kamus Anda (karena pesan kesalahan dari multiple_replace tidak terlalu memberi tahu):

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

Perhatikan bahwa itu tidak mengikat penggantian, melainkan menjalankannya secara bersamaan. Ini membuatnya lebih efisien tanpa membatasi apa yang dapat dilakukan. Untuk meniru efek rantai, Anda mungkin hanya perlu menambahkan lebih banyak pasangan pengganti-string dan memastikan urutan pasangan yang diharapkan:

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

sumber
Ini bagus, terima kasih. Mungkinkah itu ditingkatkan untuk juga memungkinkan referensi digunakan dalam penggantian? Saya belum segera menemukan cara menambahkannya.
cmarqu
Jawaban atas pertanyaan saya di atas adalah stackoverflow.com/questions/45630940/…
cmarqu
4

Inilah contoh yang lebih efisien untuk string panjang dengan banyak penggantian kecil.

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)

Intinya adalah menghindari banyak rangkaian string panjang. Kami memotong string sumber menjadi fragmen, mengganti beberapa fragmen saat kami membentuk daftar, dan kemudian menggabungkan semuanya kembali menjadi string.

9000
sumber
2

Anda seharusnya tidak melakukannya dengan cara ini, tetapi saya merasa itu terlalu keren:

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

Sekarang, answeradalah hasil dari semua penggantian pada gilirannya

sekali lagi, ini sangat hacky dan bukan sesuatu yang harus Anda gunakan secara teratur. Tapi senang mengetahui bahwa Anda dapat melakukan hal seperti ini jika perlu.

inspectorG4dget
sumber
2

Saya juga berjuang dengan masalah ini. Dengan banyak pengganti, ekspresi reguler mengalami kesulitan, dan sekitar empat kali lebih lambat daripada perulangan string.replace(dalam kondisi percobaan saya).

Anda harus benar-benar mencoba menggunakan pustaka Flashtext ( posting blog di sini , Github di sini ). Dalam kasus saya , itu sedikit lebih dari dua urutan besarnya lebih cepat, dari 1,8 detik menjadi 0,015 detik (ekspresi reguler mengambil 7,7 detik) untuk setiap dokumen.

Sangat mudah untuk menemukan contoh penggunaan di tautan di atas, tetapi ini adalah contoh yang berfungsi:

    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)

Perhatikan bahwa Flashtext membuat pergantian dalam satu pass (untuk menghindari a -> b dan b -> c menerjemahkan 'a' ke 'c'). Flashtext juga mencari seluruh kata (jadi 'is' tidak akan cocok dengan 'th is '). Ini berfungsi dengan baik jika target Anda adalah beberapa kata (mengganti 'Ini adalah' dengan 'Halo').

Pablo
sumber
Bagaimana cara kerjanya jika Anda perlu mengganti tag HTML? Misalnya ganti <p>dengan /n. Saya mencoba pendekatan Anda tetapi dengan tag flashtext sepertinya tidak menguraikannya?
alias51
1
Saya tidak yakin mengapa itu tidak berfungsi seperti yang Anda harapkan. Satu kemungkinan adalah bahwa tag ini tidak dipisahkan oleh spasi, dan ingat Flashtext mencari seluruh kata. Cara mengatasi ini adalah dengan menggunakan ganti sederhana terlebih dahulu, sehingga "Hai <p> ​​di sana" menjadi "Hai <p> ​​di sana". Anda harus berhati-hati untuk menghapus ruang yang tidak diinginkan ketika Anda selesai (juga ganti sederhana?). Semoga itu bisa membantu.
Pablo
Terima kasih, dapatkah Anda menetapkan <dan >menandai akhir kata (tetapi disertakan dalam penggantian)?
alias51
1
Saya percaya bahwa "kata-kata" hanya ditandai oleh spasi. Mungkin ada beberapa parameter opsional yang dapat Anda atur di "KeywordProcessor". Kalau tidak, pertimbangkan pendekatan di atas: gantikan "<" dengan "<", terapkan Flashtext lalu gantikan kembali (dalam kasus Anda, misalnya, "<" menjadi "<" dan "\ n" hingga "\ n" mungkin berhasil).
Pablo
2

Saya merasa pertanyaan ini membutuhkan jawaban fungsi lambda rekursif single-line untuk kelengkapan, hanya karena. Jadi disana:

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

Pemakaian:

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

Catatan:

  • Ini mengkonsumsi kamus input.
  • Dicts python mempertahankan urutan kunci pada 3.6; peringatan yang sesuai dalam jawaban lain tidak relevan lagi. Untuk kompatibilitas mundur, orang dapat menggunakan versi berbasis tuple:
>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.pop()), d)
>>> mrep('abcabc', [('a', '1'), ('c', '2')])

Catatan: Seperti halnya semua fungsi rekursif dalam python, kedalaman rekursi yang terlalu besar (yaitu kamus pengganti yang terlalu besar) akan menghasilkan kesalahan. Lihat misalnya di sini .

mcsoini
sumber
Saya mengalami RecursionError saat menggunakan kamus besar!
Pablo
@Pablo Menarik. Seberapa besar? Perhatikan bahwa ini terjadi untuk semua fungsi rekursif. Lihat misalnya di sini: stackoverflow.com/questions/3323001/…
mcsoini
Kamus substitusi saya hampir mencapai 100 ribu istilah ... sejauh ini menggunakan string.replace sejauh ini merupakan pendekatan terbaik.
Pablo
1
@Pablois dalam hal ini Anda tidak dapat menggunakan fungsi rekursif. Secara umum, sys.getrecursionlimit()adalah pasangan 1000, maks. gunakan loop atau sesuatu seperti itu, atau cobalah untuk menyederhanakan substitusi.
mcsoini
Ya, saya khawatir tidak ada jalan pintas di sini.
Pablo
1

Saya tidak tahu tentang kecepatan tapi ini perbaikan cepat hari kerja saya:

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

... tapi saya suka jawaban regex # 1 di atas. Catatan - jika satu nilai baru adalah substring dari yang lain maka operasi tidak komutatif.

del_hol
sumber
1

Anda dapat menggunakan pandaspustaka dan replacefungsi yang mendukung kedua kecocokan persis serta penggantian regex. Sebagai contoh:

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

Dan teks yang dimodifikasi adalah:

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

Anda dapat menemukan contoh di sini . Perhatikan bahwa penggantian teks dilakukan dengan urutan mereka muncul dalam daftar

George Pipis
sumber
1

Untuk mengganti hanya satu karakter, gunakan translatedanstr.maketrans adalah favorit saya.

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


demo

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.
Carson
sumber
0

Mulai dari jawaban berharga Andrew i mengembangkan skrip yang memuat kamus dari file dan menguraikan semua file pada folder yang dibuka untuk melakukan penggantian. Script memuat pemetaan dari file eksternal di mana Anda dapat mengatur pemisah. Saya seorang pemula tetapi saya menemukan skrip ini sangat berguna ketika melakukan banyak penggantian dalam beberapa file. Itu memuat kamus dengan lebih dari 1000 entri dalam hitungan detik. Itu tidak elegan tetapi berhasil untuk saya

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()
Tommaso Sandi
sumber
0

ini solusi saya untuk masalah ini. Saya menggunakannya di chatbot untuk mengganti kata-kata yang berbeda sekaligus.

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

ini akan menjadi The cat hunts the dog

emorjon2
sumber
0

Contoh lain: Daftar input

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

Output yang diinginkan adalah

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

Kode:

[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]] 
Akhil Thayyil
sumber
-2

Atau hanya untuk retasan cepat:

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)
Brandon H
sumber
-2

Berikut cara lain melakukannya dengan kamus:

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)
Stefan Gruenwald
sumber