Làm thế nào để thay thế nhiều chuỗi con của một chuỗi?


284

Tôi muốn sử dụng hàm .replace để thay thế nhiều chuỗi.

Tôi hiện đang có

string.replace("condition1", "")

nhưng muốn có một cái gì đó như

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

mặc dù điều đó không cảm thấy như cú pháp tốt

cách thích hợp để làm điều này là gì? giống như cách trong grep / regex bạn có thể làm \1\2thay thế các trường thành các chuỗi tìm kiếm nhất định


7
Bạn đã thử tất cả các giải pháp được cung cấp? Cái nào nhanh hơn?
tommy.carstensen

Tôi đã dành thời gian để kiểm tra tất cả các câu trả lời trong các tình huống khác nhau. Xem stackoverflow.com/questions/59072514/ từ
Pablo

1
Thành thật mà nói, tôi thích cách tiếp cận xích của bạn với tất cả những người khác. Tôi đã hạ cánh ở đây trong khi tìm kiếm một giải pháp và sử dụng của bạn và nó hoạt động tốt.
frakman1

@ frakman1 +1. không có lý do tại sao điều này không được nâng cao hơn. Tất cả các phương pháp khác làm cho mã khó đọc hơn. Nếu có một mảng vượt qua chức năng để thay thế, điều này sẽ hoạt động. Nhưng phương pháp xích của bạn là rõ ràng nhất (ít nhất là với số lần thay thế tĩnh)
IceFire

Câu trả lời:


269

Dưới đây là một ví dụ ngắn nên thực hiện thủ thuật với các biểu thức thông thường:

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)

Ví dụ:

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

7
Sự thay thế xảy ra trong một lần duy nhất.
Andrew Clark

26
dk vitamin: nó không quá thông minh, thậm chí nó không thông minh như vậy (chúng ta nên thoát khỏi các phím trước khi nối chúng với "|"). Tại sao điều đó không quá quan trọng? vì cách này chúng ta làm điều đó trong một đường chuyền (= nhanh), và chúng tôi làm tất cả những thay thế cùng một lúc, tránh xung đột như "spamham sha".replace("spam", "eggs").replace("sha","md5")"eggmd5m md5"thay vì"eggsham md5"
bay cừu

8
@AndrewClark Tôi sẽ đánh giá rất cao nếu bạn có thể giải thích những gì đang xảy ra trên dòng cuối cùng với lambda.
khoáng sản

11
Xin chào, tôi đã tạo ra một ý chính nhỏ với phiên bản rõ ràng hơn của đoạn trích này. Nó cũng sẽ hiệu quả hơn một chút: gist.github.com/bgusach/a967e0587d6e01e889fd1d776c5f3729
bgusach

15
Đối với python 3, sử dụng vật phẩm () thay vì iteritems ().
Jangari

127

Bạn chỉ có thể thực hiện một chức năng lặp nhỏ đẹp.

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

trong đó textlà chuỗi hoàn chỉnh và diclà một từ điển - mỗi định nghĩa là một chuỗi sẽ thay thế một kết quả khớp với thuật ngữ.

Lưu ý : trong Python 3, iteritems()đã được thay thế bằngitems()


Cẩn thận: Từ điển Python không có thứ tự đáng tin cậy để lặp lại. Giải pháp này chỉ giải quyết vấn đề của bạn nếu:

  • thứ tự thay thế là không liên quan
  • Bạn có thể thay thế để thay đổi kết quả của lần thay thế trước

Ví dụ:

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

Đầu ra có thể # 1:

"Đây là con lợn của tôi và đây là con lợn của tôi."

Sản lượng có thể # 2

"Đây là con chó của tôi và đây là con lợn của tôi."

Một cách khắc phục có thể là sử dụng 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)

Đầu ra:

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

Cẩn thận # 2: Không hiệu quả nếu textchuỗi của bạn quá lớn hoặc có nhiều cặp trong từ điển.


37
Thứ tự mà bạn áp dụng các thay thế khác nhau sẽ có vấn đề - vì vậy thay vì sử dụng một lệnh chính tả, hãy xem xét sử dụng một OrderedDict- hoặc một danh sách gồm 2 bộ dữ liệu.
lười

5
Điều này làm cho việc lặp chuỗi hai lần ... không tốt cho màn trình diễn.
Valentin Lorentz

6
Hiệu suất khôn ngoan, nó tệ hơn những gì Valentin nói - nó sẽ duyệt văn bản nhiều lần như có các mục trong dic! Tốt nếu 'văn bản' là nhỏ nhưng, khủng khiếp cho văn bản lớn.
JDonner

3
Đây là một giải pháp tốt cho một số trường hợp. Ví dụ: tôi chỉ muốn phụ 2 ký tự và tôi không quan tâm đến thứ tự chúng đi vì các phím thay thế không khớp với bất kỳ giá trị nào. Nhưng tôi muốn nó rõ ràng những gì đang xảy ra.
Nathan Garabedian

5
Lưu ý rằng điều này có thể cho kết quả bất ngờ vì văn bản mới được chèn trong lần lặp đầu tiên có thể được khớp trong lần lặp thứ hai. Ví dụ: nếu chúng ta ngây thơ cố gắng thay thế tất cả 'A' bằng 'B' và tất cả 'B' bằng 'C', chuỗi 'AB' sẽ được chuyển thành 'CC' chứ không phải 'BC'.
Ambroz Bizjak

105

Tại sao không một giải pháp như thế này?

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
Điều này là siêu hữu ích, đơn giản và di động.
Cắt nhỏ

Nhìn đẹp, nhưng không thay thế regex như trong: for r in ((r '\ s.', '.'), (R '\ s,', ',')):
Martin

2
để làm cho nó 1-liner: ss = [s.replace (* r) cho r in (("brown", "red"), ("lazy", "quick"))] [0]
Mark K

94

Đây là một biến thể của giải pháp đầu tiên sử dụng giảm, trong trường hợp bạn thích chức năng. :)

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

phiên bản thậm chí tốt hơn của martineau:

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

8
Sẽ đơn giản hơn để thực hiện replsmột chuỗi các bộ dữ liệu và loại bỏ iteritems()cuộc gọi. tức là repls = ('hello', 'goodbye'), ('world', 'earth')reduce(lambda a, kv: a.replace(*kv), repls, s). Cũng sẽ hoạt động không thay đổi trong Python 3.
martineau 5/12/13

đẹp! nếu bạn sử dụng python3, hãy sử dụng vật phẩm thay vì iteritems (hiện đã bị xóa trong công cụ dicts).
e.arbitrio

2
@martineau: Không đúng khi nó hoạt động không thay đổi trong python3 kể từ khi reducebị xóa .
Normanius

5
@normanius: reducevẫn tồn tại, tuy nhiên nó đã được tạo thành một phần của functoolsmô-đun (xem tài liệu ) trong Python 3, vì vậy khi tôi nói không thay đổi, tôi có nghĩa là cùng một mã có thể được chạy, mặc dù phải thừa nhận rằng nó sẽ reduceđược chỉnh sửa importnếu cần thiết vì nó không còn được tích hợp sẵn.
martineau

35

Đây chỉ là một bản tóm tắt ngắn gọn hơn về câu trả lời tuyệt vời của FJ và MiniQuark. Tất cả những gì bạn cần để đạt được nhiều thay thế chuỗi đồng thời là chức năng sau:

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)

Sử dụng:

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

Nếu bạn muốn, bạn có thể tạo các chức năng thay thế chuyên dụng của riêng mình bắt đầu từ chức năng đơn giản hơn này.


1
Mặc dù đây là một giải pháp tốt, nhưng việc thay thế chuỗi đồng thời sẽ không mang lại kết quả chính xác như thực hiện chúng một cách tuần tự (xâu chuỗi) chúng - mặc dù điều đó có thể không quan trọng.
martineau

2
Chắc chắn, với rep_dict = {"but": "mut", "mutton": "lamb"}chuỗi "button"kết quả trong "mutton"mã của bạn, nhưng sẽ cung cấp "lamb"nếu thay thế được nối tiếp, lần lượt từng chuỗi.
martineau

2
Đó là tính năng chính của mã này, không phải là một khiếm khuyết. Với sự thay thế chuỗi, nó không thể đạt được hành vi mong muốn thay thế hai từ đồng thời và đối ứng như trong ví dụ của tôi.
mmj

1
Nó dường như không phải là một tính năng tuyệt vời nếu bạn không cần nó. Nhưng ở đây chúng ta đang nói về việc thay thế đồng thời , thì đó thực sự là tính năng chính. Với sự thay thế "bị xiềng xích", đầu ra của ví dụ sẽ là Do you prefer cafe? No, I prefer cafe., điều này hoàn toàn không đáng tin cậy.
mmj

@David viết câu trả lời của riêng bạn, chỉnh sửa của bạn quá triệt để
UmNyobe

29

Tôi đã xây dựng điều này dựa trên câu trả lời xuất sắc của 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)

Sử dụng một lần bắn:

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

Lưu ý rằng vì việc thay thế được thực hiện chỉ trong một lần, "café" đổi thành "tea", nhưng nó không đổi lại thành "café".

Nếu bạn cần thực hiện thay thế tương tự nhiều lần, bạn có thể tạo chức năng thay thế dễ dàng:

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

Cải tiến:

  • biến mã thành một hàm
  • thêm hỗ trợ đa dòng
  • sửa lỗi thoát
  • dễ dàng tạo một hàm cho một sự thay thế cụ thể

Thưởng thức! :-)


1
Ai đó có thể giải thích điều này từng bước cho những con trăn như tôi không?
Julian Suarez

Đồng nghiệp trăn noob ở đây, vì vậy tôi sẽ thực hiện một cú bắn không hoàn chỉnh để hiểu nó .. a. chia nhỏ key_values ​​thành công cụ thay thế (các khóa được nối bởi "|") và logic (nếu khớp là khóa, giá trị trả về) b. tạo một trình phân tích cú pháp regex ("mẫu" tìm kiếm các khóa và sử dụng logic đã cho) - bọc cái này trong hàm lambda và trả về. Thứ tôi đang tìm kiếm bây giờ: re.M, và sự cần thiết cho lambda cho logic thay thế.
Cáo

1
@Fox Bạn hiểu rồi. Bạn có thể định nghĩa một hàm thay vì sử dụng lambda, nó chỉ để làm cho mã ngắn hơn. Nhưng lưu ý rằng pattern.submong đợi một chức năng chỉ có một tham số (văn bản để thay thế), vì vậy chức năng này cần phải có quyền truy cập replace_dict. re.Mcho phép thay thế Multiline (nó được giải thích rõ trong tài liệu: docs.python.org/2/l Library / re.html # re.M ).
MiniQuark

22

Tôi muốn đề xuất việc sử dụng các mẫu chuỗi. Chỉ cần đặt chuỗi được thay thế trong một từ điển và tất cả được thiết lập! Ví dụ từ 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'

Có vẻ tốt, nhưng khi thêm một khóa không được cung cấp substitutesẽ làm tăng ngoại lệ, vì vậy hãy cẩn thận khi lấy mẫu từ người dùng.
Bart Friederichs

2
Một nhược điểm của phương pháp này là mẫu phải chứa tất cả, và không nhiều hơn tất cả, chuỗi $ được thay thế, xem tại đây
RolfBly

17

Trong trường hợp của tôi, tôi cần một sự thay thế đơn giản các khóa duy nhất bằng tên, vì vậy tôi nghĩ điều này:

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ều này hoạt động miễn là bạn không có một cuộc đụng độ thay thế. Nếu bạn thay thế ibằng sbạn sẽ có một hành vi kỳ lạ.
bgusach

1
Nếu thứ tự là quan trọng, thay vì dict ở trên, bạn có thể sử dụng một mảng: b = [ ['i', 'Z'], ['s', 'Y'] ]; for x,y in (b): a = a.replace(x, y) Sau đó, nếu bạn cẩn thận sắp xếp các cặp mảng của mình, bạn có thể đảm bảo bạn không thay thế () theo cách đệ quy.
MÃ-REaD

Dường như các dicts hiện duy trì trật tự , từ Python 3.7.0. Tôi đã thử nghiệm nó và nó hoạt động theo thứ tự trên máy của tôi với Python ổn định mới nhất 3.
James Koss

15

Bắt đầu Python 3.8và giới thiệu các biểu thức gán (PEP 572) ( :=toán tử), chúng ta có thể áp dụng các thay thế trong phạm vi hiểu danh sách:

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

Bạn có biết nếu điều này là hiệu quả hơn so với việc sử dụng thay thế trong một vòng lặp? Tôi đang kiểm tra tất cả các câu trả lời cho hiệu suất nhưng tôi chưa có 3,8.
Pablo

Tại sao tôi nhận được đầu ra trong một danh sách?
johnrao07

1
@ johnrao07 Vâng, một sự hiểu biết danh sách xây dựng một danh sách. Đó là lý do tại sao, trong trường hợp này, bạn nhận được ['The quick red fox jumps over the lazy dog', 'The quick red fox jumps over the quick dog']. Nhưng biểu thức gán ( text := text.replace) cũng lặp lại xây dựng các phiên bản mới textbằng cách thay đổi nó. Sau khi hiểu danh sách, bạn có thể sử dụng textbiến chứa văn bản đã sửa đổi.
Xavier Guihot

1
Nếu bạn muốn trả về phiên bản mới textdưới dạng một lớp lót, bạn cũng có thể sử dụng [text := text.replace(a, b) for a, b in replacements][-1](lưu ý [-1]), trích xuất phần tử cuối cùng của việc hiểu danh sách; tức là phiên bản cuối cùng của text.
Xavier Guihot

13

Đây là 0,02 đô la của tôi. Nó dựa trên câu trả lời của Andrew Clark, rõ ràng hơn một chút và nó cũng bao gồm trường hợp khi một chuỗi cần thay thế là một chuỗi con của một chuỗi khác để thay thế (chuỗi dài hơn sẽ thắng)

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)

Đó là trong ý chính này , vui lòng sửa đổi nó nếu bạn có bất kỳ đề xuất.


1
Thay vào đó, đây phải là câu trả lời được chấp nhận vì regex được xây dựng từ tất cả các khóa bằng cách sắp xếp chúng theo thứ tự chiều dài giảm dần và nối chúng với | toán tử luân phiên regex. Và việc sắp xếp là cần thiết để lựa chọn dài nhất trong tất cả các lựa chọn có thể được chọn nếu có bất kỳ lựa chọn thay thế nào.
Sachin S

Tôi đồng ý rằng đây là giải pháp tốt nhất, nhờ vào việc phân loại. Ngoài việc sắp xếp giống hệt với câu trả lời ban đầu của tôi, vì vậy tôi cũng đã mượn cách sắp xếp cho giải pháp của mình, để đảm bảo không ai bỏ lỡ một tính năng quan trọng như vậy.
mmj

6

Tôi cần một giải pháp trong đó các chuỗi được thay thế có thể là một biểu thức thông thường, ví dụ để giúp bình thường hóa một văn bản dài bằng cách thay thế nhiều ký tự khoảng trắng bằng một ký tự. Dựa trên một chuỗi câu trả lời từ những người khác, bao gồm MiniQuark và mmj, đây là những gì tôi nghĩ ra:

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)

Nó hoạt động cho các ví dụ được đưa ra trong các câu trả lời khác, ví dụ:

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

Điều chính đối với tôi là bạn cũng có thể sử dụng các biểu thức thông thường, ví dụ để chỉ thay thế toàn bộ các từ hoặc để bình thường hóa khoảng trắng:

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

Nếu bạn muốn sử dụng các khóa từ điển như các chuỗi bình thường, bạn có thể thoát các khóa đó trước khi gọi nhiều numplace bằng cách sử dụng chức năng này:

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"

Hàm sau có thể giúp tìm các biểu thức chính quy sai trong số các khóa từ điển của bạn (vì thông báo lỗi từ nhiều bit không phải là rất hay):

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

Lưu ý rằng nó không xâu chuỗi các thay thế, thay vào đó thực hiện chúng đồng thời. Điều này làm cho nó hiệu quả hơn mà không hạn chế những gì nó có thể làm. Để bắt chước hiệu ứng của chuỗi, bạn có thể chỉ cần thêm nhiều cặp thay thế chuỗi và đảm bảo thứ tự dự kiến ​​của các cặp:

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

Điều này là tốt đẹp, cảm ơn. Nó có thể được cải thiện để cũng cho phép phản ứng ngược được sử dụng trong các thay thế? Tôi đã không ngay lập tức tìm ra cách để thêm nó.
cmarqu

Câu trả lời cho câu hỏi của tôi ở trên là stackoverflow.com/questions/45630940/ từ
cmarqu

4

Đây là một mẫu hiệu quả hơn trên các chuỗi dài với nhiều thay thế nhỏ.

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)

Vấn đề là tránh nhiều chuỗi dài. Chúng tôi cắt chuỗi nguồn thành các đoạn, thay thế một số đoạn khi chúng tôi tạo thành danh sách, sau đó nối toàn bộ lại thành chuỗi.


2

Bạn thực sự không nên làm theo cách này, nhưng tôi chỉ thấy nó quá tuyệt:

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

Bây giờ, answerlà kết quả của tất cả các thay thế lần lượt

một lần nữa, điều này rất hack và không phải là thứ mà bạn nên sử dụng thường xuyên. Nhưng thật tuyệt khi biết rằng bạn có thể làm điều gì đó như thế này nếu bạn cần.


2

Tôi đã đấu tranh với vấn đề này là tốt. Với nhiều sự thay thế biểu thức thông thường đấu tranh, và chậm hơn khoảng bốn lần so với vòng lặp string.replace(trong điều kiện thử nghiệm của tôi).

Bạn hoàn toàn nên thử sử dụng thư viện Flashtext ( bài đăng blog ở đây , Github tại đây ). Trong trường hợp của tôi , tốc độ nhanh hơn hai bậc một chút , từ 1,8 giây đến 0,015 giây (biểu thức chính quy mất 7,7 giây) cho mỗi tài liệu.

Rất dễ tìm thấy các ví dụ sử dụng trong các liên kết ở trên, nhưng đây là một ví dụ hoạt động:

    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)

Lưu ý rằng Flashtext thực hiện thay thế trong một lần duy nhất (để tránh a -> bb -> c dịch 'a' thành 'c'). Flashtext cũng tìm toàn bộ các từ (vì vậy 'là' sẽ không khớp với ' '). Nó hoạt động tốt nếu mục tiêu của bạn là một vài từ (thay thế 'Đây là' bằng 'Xin chào').


Làm thế nào để nó hoạt động nếu bạn cần thay thế các thẻ HTML? Ví dụ thay thế <p>bằng /n. Tôi đã thử cách tiếp cận của bạn nhưng với các thẻ flashtext dường như không phân tích được nó?
bí danh51

1
Tôi không chắc tại sao nó không hoạt động như bạn mong đợi. Một khả năng là các thẻ này không được phân tách bằng dấu cách và hãy nhớ Flashtext tìm toàn bộ từ. Cách khác là trước tiên hãy sử dụng một thay thế đơn giản để "Hi <p> ​​there" trở thành "Hi <p> ​​there". Bạn sẽ cần phải cẩn thận để loại bỏ các không gian không mong muốn khi bạn hoàn thành (thay thế đơn giản?). Mong rằng sẽ giúp.
Pablo

Cảm ơn, bạn có thể đặt <>đánh dấu kết thúc của một từ (nhưng được bao gồm trong thay thế)?
bí danh51

1
Tôi tin rằng "từ" chỉ được đánh dấu bằng khoảng trắng. Có lẽ có một số tham số tùy chọn bạn có thể đặt trong "KeywordProcessor". Mặt khác, hãy xem xét cách tiếp cận ở trên: thay thế "<" bằng "<", áp dụng Flashtext sau đó thay thế trở lại (ví dụ: trong trường hợp của bạn, "<" thành "<" và "\ n" thành "\ n" có thể hoạt động).
Pablo

2

Tôi cảm thấy câu hỏi này cần một câu trả lời hàm lambda đệ quy một dòng cho đầy đủ, chỉ vì. Vì vậy, có

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

Sử dụng:

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

Ghi chú:

  • Điều này tiêu thụ từ điển đầu vào.
  • Python dicts giữ trật tự khóa kể từ 3.6; hãy cẩn thận trong các câu trả lời khác không còn phù hợp nữa. Để tương thích ngược, người ta có thể sử dụng phiên bản dựa trên bộ dữ liệu:
>>> mrep = lambda s, d: s if not d else mrep(s.replace(*d.pop()), d)
>>> mrep('abcabc', [('a', '1'), ('c', '2')])

Lưu ý: Như với tất cả các hàm đệ quy trong python, độ sâu đệ quy quá lớn (nghĩa là từ điển thay thế quá lớn) sẽ dẫn đến lỗi. Xem ví dụ ở đây .


Tôi chạy vào RecursionError khi sử dụng một từ điển lớn!
Pablo

@Pablo Thú vị. Lớn bao nhiêu? Lưu ý rằng điều này xảy ra cho tất cả các hàm đệ quy. Xem ví dụ tại đây: stackoverflow.com/questions/3323001/
Ấn

Từ điển thay thế của tôi gần với 100 nghìn thuật ngữ ... cho đến nay sử dụng chuỗi.replace là cách tiếp cận tốt nhất.
Pablo

1
@Pablo trong trường hợp đó bạn không thể sử dụng các hàm đệ quy. Nói chung, sys.getrecursionlimit()là một cặp 1000, tối đa. sử dụng một vòng lặp hoặc một cái gì đó tương tự, hoặc cố gắng đơn giản hóa các thay thế.
mcsoini

Vâng, tôi sợ thực sự không có lối tắt ở đây.
Pablo

1

Tôi không biết về tốc độ nhưng đây là cách khắc phục nhanh trong công việc của tôi:

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

... nhưng tôi thích câu trả lời regex số 1 ở trên. Lưu ý - nếu một giá trị mới là một chuỗi con của một giá trị khác thì thao tác không giao hoán.


1

Bạn có thể sử dụng pandasthư viện và replacechức năng hỗ trợ cả khớp chính xác cũng như thay thế regex. Ví dụ:

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

Và văn bản sửa đổi là:

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

Bạn có thể tìm thấy một ví dụ ở đây . Lưu ý rằng việc thay thế trên văn bản được thực hiện theo thứ tự chúng xuất hiện trong danh sách


1

Để chỉ thay thế một ký tự, sử dụng translatestr.maketranslà phương pháp yêu thích của tôi.

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


bản giới thiệu

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

Bắt đầu từ câu trả lời quý giá của Andrew i đã phát triển một tập lệnh tải từ điển từ một tệp và xây dựng tất cả các tệp trên thư mục đã mở để thực hiện thay thế. Kịch bản tải các ánh xạ từ một tệp bên ngoài mà bạn có thể đặt dấu phân cách. Tôi là người mới bắt đầu nhưng tôi thấy tập lệnh này rất hữu ích khi thực hiện nhiều thay thế trong nhiều tệp. Nó tải một từ điển với hơn 1000 mục trong vài giây. Nó không thanh lịch nhưng nó làm việc cho tôi

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

đây là giải pháp của tôi cho vấn đề Tôi đã sử dụng nó trong một chatbot để thay thế các từ khác nhau cùng một lúc.

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

điều này sẽ trở thành The cat hunts the dog


0

Một ví dụ khác: Danh sách đầu vào

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

Đầu ra mong muốn sẽ là

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

Mã số:

[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

Hoặc chỉ để hack nhanh:

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

Đây là một cách khác để làm điều đó với một từ điển:

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.