Cách tốt nhất để thay thế nhiều ký tự trong một chuỗi?


Câu trả lời:


432

Thay thế hai ký tự

Tôi đã tính thời gian cho tất cả các phương thức trong các câu trả lời hiện tại cùng với một phương pháp bổ sung.

Với chuỗi đầu vào abc&def#ghivà thay thế & -> \ & và # -> \ #, cách nhanh nhất là xâu chuỗi các thay thế như thế này : text.replace('&', '\&').replace('#', '\#').

Thời gian cho từng chức năng:

  • a) 1000000 vòng, tốt nhất là 3: 1,47 μs trên mỗi vòng lặp
  • b) 1000000 vòng, tốt nhất là 3: 1,51 μs trên mỗi vòng lặp
  • c) 100000 vòng, tốt nhất là 3: 12,3 μs trên mỗi vòng lặp
  • d) 100000 vòng, tốt nhất là 3: 12 ss mỗi vòng
  • e) 100000 vòng, tốt nhất là 3: 3,27 μs trên mỗi vòng lặp
  • f) 1000000 vòng, tốt nhất là 3: 0.817 μs trên mỗi vòng lặp
  • g) 100000 vòng, tốt nhất là 3: 3,64 per mỗi vòng
  • h) 1000000 vòng, tốt nhất là 3: 0,927 μs mỗi vòng
  • i) 1000000 vòng, tốt nhất là 3: 0.814 μs trên mỗi vòng lặp

Dưới đây là các chức năng:

def a(text):
    chars = "&#"
    for c in chars:
        text = text.replace(c, "\\" + c)


def b(text):
    for ch in ['&','#']:
        if ch in text:
            text = text.replace(ch,"\\"+ch)


import re
def c(text):
    rx = re.compile('([&#])')
    text = rx.sub(r'\\\1', text)


RX = re.compile('([&#])')
def d(text):
    text = RX.sub(r'\\\1', text)


def mk_esc(esc_chars):
    return lambda s: ''.join(['\\' + c if c in esc_chars else c for c in s])
esc = mk_esc('&#')
def e(text):
    esc(text)


def f(text):
    text = text.replace('&', '\&').replace('#', '\#')


def g(text):
    replacements = {"&": "\&", "#": "\#"}
    text = "".join([replacements.get(c, c) for c in text])


def h(text):
    text = text.replace('&', r'\&')
    text = text.replace('#', r'\#')


def i(text):
    text = text.replace('&', r'\&').replace('#', r'\#')

Thời gian như thế này:

python -mtimeit -s"import time_functions" "time_functions.a('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.b('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.c('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.d('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.e('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.f('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.g('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.h('abc&def#ghi')"
python -mtimeit -s"import time_functions" "time_functions.i('abc&def#ghi')"

Thay thế 17 ký tự

Đây là mã tương tự để làm tương tự nhưng có nhiều ký tự để thoát (\ `* _ {}> # + -.! $):

def a(text):
    chars = "\\`*_{}[]()>#+-.!$"
    for c in chars:
        text = text.replace(c, "\\" + c)


def b(text):
    for ch in ['\\','`','*','_','{','}','[',']','(',')','>','#','+','-','.','!','$','\'']:
        if ch in text:
            text = text.replace(ch,"\\"+ch)


import re
def c(text):
    rx = re.compile('([&#])')
    text = rx.sub(r'\\\1', text)


RX = re.compile('([\\`*_{}[]()>#+-.!$])')
def d(text):
    text = RX.sub(r'\\\1', text)


def mk_esc(esc_chars):
    return lambda s: ''.join(['\\' + c if c in esc_chars else c for c in s])
esc = mk_esc('\\`*_{}[]()>#+-.!$')
def e(text):
    esc(text)


def f(text):
    text = text.replace('\\', '\\\\').replace('`', '\`').replace('*', '\*').replace('_', '\_').replace('{', '\{').replace('}', '\}').replace('[', '\[').replace(']', '\]').replace('(', '\(').replace(')', '\)').replace('>', '\>').replace('#', '\#').replace('+', '\+').replace('-', '\-').replace('.', '\.').replace('!', '\!').replace('$', '\$')


def g(text):
    replacements = {
        "\\": "\\\\",
        "`": "\`",
        "*": "\*",
        "_": "\_",
        "{": "\{",
        "}": "\}",
        "[": "\[",
        "]": "\]",
        "(": "\(",
        ")": "\)",
        ">": "\>",
        "#": "\#",
        "+": "\+",
        "-": "\-",
        ".": "\.",
        "!": "\!",
        "$": "\$",
    }
    text = "".join([replacements.get(c, c) for c in text])


def h(text):
    text = text.replace('\\', r'\\')
    text = text.replace('`', r'\`')
    text = text.replace('*', r'\*')
    text = text.replace('_', r'\_')
    text = text.replace('{', r'\{')
    text = text.replace('}', r'\}')
    text = text.replace('[', r'\[')
    text = text.replace(']', r'\]')
    text = text.replace('(', r'\(')
    text = text.replace(')', r'\)')
    text = text.replace('>', r'\>')
    text = text.replace('#', r'\#')
    text = text.replace('+', r'\+')
    text = text.replace('-', r'\-')
    text = text.replace('.', r'\.')
    text = text.replace('!', r'\!')
    text = text.replace('$', r'\$')


def i(text):
    text = text.replace('\\', r'\\').replace('`', r'\`').replace('*', r'\*').replace('_', r'\_').replace('{', r'\{').replace('}', r'\}').replace('[', r'\[').replace(']', r'\]').replace('(', r'\(').replace(')', r'\)').replace('>', r'\>').replace('#', r'\#').replace('+', r'\+').replace('-', r'\-').replace('.', r'\.').replace('!', r'\!').replace('$', r'\$')

Đây là kết quả cho cùng một chuỗi đầu vào abc&def#ghi:

  • a) 100000 vòng, tốt nhất là 3: 6,72 ss mỗi vòng
  • b) 100000 vòng, tốt nhất là 3: 2,64 ss mỗi vòng
  • c) 100000 vòng, tốt nhất là 3: 11,9 per mỗi vòng
  • d) 100000 vòng, tốt nhất là 3: 4,92 μ mỗi vòng
  • e) 100000 vòng, tốt nhất là 3: 2,96 μs trên mỗi vòng lặp
  • f) 100000 vòng, tốt nhất là 3: 4,29 μs trên mỗi vòng lặp
  • g) 100000 vòng, tốt nhất là 3: 4,68 μs trên mỗi vòng lặp
  • h) 100000 vòng, tốt nhất là 3: 4,73 per mỗi vòng
  • i) 100000 vòng, tốt nhất là 3: 4,24 ss mỗi vòng

Và với chuỗi đầu vào dài hơn ( ## *Something* and [another] thing in a longer sentence with {more} things to replace$):

  • a) 100000 vòng, tốt nhất là 3: 7,59 μs mỗi vòng
  • b) 100000 vòng, tốt nhất là 3: 6,54 ss mỗi vòng
  • c) 100000 vòng, tốt nhất là 3: 16,9 μs trên mỗi vòng lặp
  • d) 100000 vòng, tốt nhất là 3: 7,29 μs trên mỗi vòng lặp
  • e) 100000 vòng, tốt nhất là 3: 12,2 μs trên mỗi vòng lặp
  • f) 100000 vòng, tốt nhất là 3: 5,38 ss mỗi vòng
  • g) 10000 vòng, tốt nhất là 3: 21,7 pers trên mỗi vòng lặp
  • h) 100000 vòng, tốt nhất là 3: 5,7 per mỗi vòng
  • i) 100000 vòng, tốt nhất là 3: 5,13 μs trên mỗi vòng lặp

Thêm một vài biến thể:

def ab(text):
    for ch in ['\\','`','*','_','{','}','[',']','(',')','>','#','+','-','.','!','$','\'']:
        text = text.replace(ch,"\\"+ch)


def ba(text):
    chars = "\\`*_{}[]()>#+-.!$"
    for c in chars:
        if c in text:
            text = text.replace(c, "\\" + c)

Với đầu vào ngắn hơn:

  • ab) 100000 vòng, tốt nhất là 3: 7,05 μs trên mỗi vòng lặp
  • ba) 100000 vòng, tốt nhất là 3: 2,4 μs trên mỗi vòng lặp

Với đầu vào dài hơn:

  • ab) 100000 vòng, tốt nhất là 3: 7,71 ss mỗi vòng
  • ba) 100000 vòng, tốt nhất là 3: 6.08 μs trên mỗi vòng lặp

Vì vậy, tôi sẽ sử dụng bacho khả năng đọc và tốc độ.

Phụ lục

Được nhắc nhở bởi haccks trong các ý kiến, một sự khác biệt giữa abbaif c in text:kiểm tra. Hãy thử nghiệm chúng với hai biến thể khác:

def ab_with_check(text):
    for ch in ['\\','`','*','_','{','}','[',']','(',')','>','#','+','-','.','!','$','\'']:
        if ch in text:
            text = text.replace(ch,"\\"+ch)

def ba_without_check(text):
    chars = "\\`*_{}[]()>#+-.!$"
    for c in chars:
        text = text.replace(c, "\\" + c)

Thời gian tính bằng per trên mỗi vòng lặp trên Python 2.7.14 và 3.6.3 và trên một máy khác với bộ trước đó, do đó không thể so sánh trực tiếp.

╭────────────╥──────┬───────────────┬──────┬──────────────────╮
 Py, input    ab   ab_with_check   ba   ba_without_check 
╞════════════╬══════╪═══════════════╪══════╪══════════════════╡
 Py2, short  8.81     4.22        3.45     8.01          
 Py3, short  5.54     1.34        1.46     5.34          
├────────────╫──────┼───────────────┼──────┼──────────────────┤
 Py2, long   9.3      7.15        6.85     8.55          
 Py3, long   7.43     4.38        4.41     7.02          
└────────────╨──────┴───────────────┴──────┴──────────────────┘

Chúng ta có thể kết luận rằng:

  • Những người có séc nhanh hơn gấp 4 lần so với những người không có séc

  • ab_with_checkhơi dẫn đầu về Python 3, nhưng ba(có kiểm tra) có lượng khách hàng tiềm năng lớn hơn trên Python 2

  • Tuy nhiên, bài học lớn nhất ở đây là Python 3 nhanh hơn gấp 3 lần so với Python 2 ! Không có sự khác biệt lớn giữa chậm nhất trên Python 3 và nhanh nhất trên Python 2!


4
Tại sao đây không phải là câu trả lời bị loại trừ?
Súp gà

if c in text:cần thiết trong ba?
haccks

@haccks Không cần thiết, nhưng nó nhanh hơn 2-3 lần với nó. Chuỗi ngắn, có: 1.45 usec per loopvà không có : 5.3 usec per loop, Chuỗi dài, có: 4.38 usec per loopvà không có : 7.03 usec per loop. (Lưu ý những điều này không thể so sánh trực tiếp với kết quả ở trên, vì đó là một máy khác, v.v.)
Hugo

1
@Hugo; Tôi nghĩ rằng sự khác biệt về thời gian này là do replacechỉ được gọi khi cđược tìm thấy texttrong trường hợp batrong khi nó được gọi trong mỗi lần lặp trong ab.
haccks

2
@haccks Cảm ơn, tôi đã cập nhật câu trả lời của mình với thời gian tiếp theo: thêm kiểm tra sẽ tốt hơn cho cả hai, nhưng bài học lớn nhất là Python 3 nhanh hơn gấp 3 lần!
Hugo

73
>>> string="abc&def#ghi"
>>> for ch in ['&','#']:
...   if ch in string:
...      string=string.replace(ch,"\\"+ch)
...
>>> print string
abc\&def\#ghi

Tại sao một dấu gạch chéo kép cần thiết? Tại sao không "\" hoạt động?
axolotl

3
Dấu gạch chéo kép thoát khỏi dấu gạch chéo ngược, nếu không, python sẽ hiểu "\" là một ký tự trích dẫn bằng chữ trong một chuỗi vẫn mở.
Riet

Tại sao bạn cần phải string=string.replace(ch,"\\"+ch)? Không string.replace(ch,"\\"+ch)đủ sao?
MattSom

1
@MattSom thay thế () không sửa đổi chuỗi gốc, nhưng trả về một bản sao. Vì vậy, bạn cần gán cho mã để có bất kỳ hiệu lực.
Ben Brian

3
Bạn có thực sự cần nếu? Nó trông giống như một bản sao của những gì người thay thế sẽ làm gì.
lorenzo

32

Đơn giản chỉ cần chuỗi các replacechức năng như thế này

strs = "abc&def#ghi"
print strs.replace('&', '\&').replace('#', '\#')
# abc\&def\#ghi

Nếu sự thay thế sẽ có số lượng nhiều hơn, bạn có thể làm điều này theo cách chung này

strs, replacements = "abc&def#ghi", {"&": "\&", "#": "\#"}
print "".join([replacements.get(c, c) for c in strs])
# abc\&def\#ghi

28

Đây là một phương pháp python3 sử dụng str.translatestr.maketrans:

s = "abc&def#ghi"
print(s.translate(str.maketrans({'&': '\&', '#': '\#'})))

Chuỗi in là abc\&def\#ghi.


2
Đây là một câu trả lời tốt, nhưng trong thực tế, một người .translate()dường như chậm hơn ba chuỗi .replace()(sử dụng CPython 3.6.4).
Changaco

@Changaco Cảm ơn vì đã định thời gian Trong thực tế tôi sẽ sử dụng replace()bản thân mình, nhưng tôi đã thêm câu trả lời này vì mục đích hoàn chỉnh.
tommy.carstensen

Đối với các chuỗi lớn và nhiều thay thế, việc này sẽ nhanh hơn, mặc dù một số thử nghiệm sẽ rất tốt ...
Graodes

Vâng, nó không có trên máy của tôi (tương tự cho 2 và 17 thay thế).
Graodes

Làm thế nào là '\#'hợp lệ? không nên r'\#'hay '\\#'sao? Có thể là một vấn đề định dạng khối mã có lẽ.
chẵn lẻ3

16

Bạn luôn luôn chuẩn bị một dấu gạch chéo ngược? Nếu vậy, hãy thử

import re
rx = re.compile('([&#])')
#                  ^^ fill in the characters here.
strs = rx.sub('\\\\\\1', strs)

Nó có thể không phải là phương pháp hiệu quả nhất nhưng tôi nghĩ nó là dễ nhất.


15
thử aarrgghhr'\\\1'
John Machin

10

Đến bữa tiệc muộn, nhưng tôi đã mất rất nhiều thời gian với vấn đề này cho đến khi tôi tìm thấy câu trả lời của mình.

Ngắn và ngọt ngào, translatelà vượt trộireplace . Nếu bạn quan tâm nhiều hơn đến chức năng tối ưu hóa theo thời gian, không sử dụngreplace .

Đồng thời sử dụng translatenếu bạn không biết liệu bộ ký tự được thay thế có chồng lấp bộ ký tự được sử dụng để thay thế hay không.

Trường hợp tại điểm:

Sử dụng replacebạn sẽ ngây thơ mong đợi đoạn trích "1234".replace("1", "2").replace("2", "3").replace("3", "4")trở lại "2344", nhưng thực tế nó sẽ trở lại "4444".

Dịch dường như thực hiện những gì OP mong muốn ban đầu.


6

Bạn có thể xem xét viết một hàm thoát chung:

def mk_esc(esc_chars):
    return lambda s: ''.join(['\\' + c if c in esc_chars else c for c in s])

>>> esc = mk_esc('&#')
>>> print esc('Learn & be #1')
Learn \& be \#1

Bằng cách này, bạn có thể làm cho chức năng của mình có thể cấu hình được với một danh sách các ký tự cần được thoát.


3

FYI, điều này ít hoặc không được sử dụng cho OP nhưng nó có thể được sử dụng cho những người đọc khác (xin đừng downvote, tôi biết điều này).

Là một bài tập hơi vô lý nhưng thú vị, muốn xem liệu tôi có thể sử dụng lập trình chức năng python để thay thế nhiều ký tự không. Tôi khá chắc chắn rằng điều này KHÔNG đánh bại chỉ gọi thay thế () hai lần. Và nếu hiệu suất là một vấn đề, bạn có thể dễ dàng đánh bại điều này trong rỉ sét, C, julia, perl, java, javascript và thậm chí có thể là awk. Nó sử dụng gói 'người trợ giúp' bên ngoài được gọi là pytoolz , được tăng tốc thông qua cython ( cytoolz, đó là gói pypi ).

from cytoolz.functoolz import compose
from cytoolz.itertoolz import chain,sliding_window
from itertools import starmap,imap,ifilter
from operator import itemgetter,contains
text='&hello#hi&yo&'
char_index_iter=compose(partial(imap, itemgetter(0)), partial(ifilter, compose(partial(contains, '#&'), itemgetter(1))), enumerate)
print '\\'.join(imap(text.__getitem__, starmap(slice, sliding_window(2, chain((0,), char_index_iter(text), (len(text),))))))

Tôi thậm chí sẽ không giải thích điều này bởi vì không ai sẽ sử dụng điều này để thực hiện nhiều thay thế. Tuy nhiên, tôi cảm thấy hơi thành công khi làm điều này và nghĩ rằng nó có thể truyền cảm hứng cho những độc giả khác hoặc giành chiến thắng trong một cuộc thi giấu mã.


1
"lập trình chức năng" không có nghĩa là "sử dụng càng nhiều chức năng càng tốt", bạn biết đấy.
Craig Andrew

1
Đây là một công cụ thay thế đa char hoàn toàn tốt, thuần túy: gist.github.com/anonymous/4577424f586173fc6b91a215ea2ce89e Không phân bổ, không đột biến, không tác dụng phụ. Có thể đọc được, quá.
Craig Andrew

1

Sử dụng giảm có sẵn trong python2.7 và python3. * Bạn có thể dễ dàng thay thế các chuỗi con một cách sạch sẽ và pythonic.

# Lets define a helper method to make it easy to use
def replacer(text, replacements):
    return reduce(
        lambda text, ptuple: text.replace(ptuple[0], ptuple[1]), 
        replacements, text
    )

if __name__ == '__main__':
    uncleaned_str = "abc&def#ghi"
    cleaned_str = replacer(uncleaned_str, [("&","\&"),("#","\#")])
    print(cleaned_str) # "abc\&def\#ghi"

Trong python2.7, bạn không phải nhập giảm nhưng trong python3. * Bạn phải nhập nó từ mô-đun funcools.


1

Có thể một vòng lặp đơn giản cho ký tự thay thế:

a = '&#'

to_replace = ['&', '#']

for char in to_replace:
    a = a.replace(char, "\\"+char)

print(a)

>>> \&\#

1

Còn cái này thì sao?

def replace_all(dict, str):
    for key in dict:
        str = str.replace(key, dict[key])
    return str

sau đó

print(replace_all({"&":"\&", "#":"\#"}, "&#"))

đầu ra

\&\#

tương tự như câu trả lời


0
>>> a = '&#'
>>> print a.replace('&', r'\&')
\&#
>>> print a.replace('#', r'\#')
&\#
>>> 

Bạn muốn sử dụng chuỗi 'thô' (được biểu thị bằng tiền tố 'r' tiền tố thay thế), vì các chuỗi thô không xử lý đặc biệt dấu gạch chéo ngược.

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.