문자열에서 여러 문자를 바꾸는 가장 좋은 방법은 무엇입니까?


답변:


434

두 문자 교체

나는 현재 답변의 모든 방법을 하나 더 추가했습니다.

abc&def#ghi&-> \ & 및 #-> \ # 의 입력 문자열을 사용 하여 대체하는 가장 빠른 방법은 다음과 같이 대체를 연결하는 것 text.replace('&', '\&').replace('#', '\#')입니다.

각 기능의 타이밍 :

  • a) 1000000 루프, 루프 당 3 : 1.47 μs 최고
  • b) 1000000 루프, 루프 당 3 : 1 최고 1.51 μs
  • c) 100000 루프, 루프 당 3 : 12.3 μs
  • d) 100000 루프, 루프 당 3:12 μs 중 최고
  • e) 100000 루프, 루프 당 3 : 3.27 μs 중 최고
  • f) 1000000 루프, 루프 당 3 : 0.817 μs
  • g) 100000 루프, 루프 당 3 : 3.64 μs 중 최고
  • h) 1000000 루프, 루프 당 3 : 0.927 μs
  • i) 1000000 루프, 루프 당 3 : 최고 0.814 μs

기능은 다음과 같습니다.

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

이 같은 시간 :

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

17 자 교체

다음은 동일하지만 이스케이프 할 문자가 더 많은 유사한 코드입니다 (\`* _ {}> # +-.! $) :

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'\$')

동일한 입력 문자열에 대한 결과는 다음과 같습니다 abc&def#ghi.

  • a) 100000 루프, 루프 당 3 : 6.72 μs 최고
  • b) 100000 루프, 루프 당 3 : 3.64 μs 중 최고
  • c) 100000 루프, 루프 당 3 : 9.9 μs 중 최고
  • d) 100000 루프, 루프 당 3 : 3.92 μs 최고
  • e) 100000 루프, 루프 당 3 : 3.96 μs 최고
  • f) 100000 루프, 루프 당 3 : 3.29 μs 최고
  • g) 100000 루프, 루프 당 3 : 3.68 μs 중 최고
  • h) 100000 루프, 루프 당 3 : 3.73 μs 중 최고
  • i) 100000 루프, 루프 당 3 : 3.24 μs 최고

그리고 더 긴 입력 문자열 ( ## *Something* and [another] thing in a longer sentence with {more} things to replace$)

  • a) 100000 루프, 루프 당 3 : 59.59 μs
  • b) 100000 루프, 루프 당 3 : 6.54 μs
  • c) 100000 루프, 루프 당 최고 3 : 16.9 μs
  • d) 100000 루프, 루프 당 3 : 7.29 μs 최고
  • e) 100000 루프, 루프 당 3 : 12.2 μs 중 최고
  • f) 100000 루프, 루프 당 3 : 5.38 μs 최고
  • g) 10000 루프, 루프 당 3 : 3 최고 21.7 μs
  • h) 100000 루프, 루프 당 3 : 3 최고 5.7 μs
  • i) 100000 루프, 루프 당 3 : 5.13 μs 최고

몇 가지 변형 추가 :

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)

더 짧은 입력으로 :

  • ab) 100000 루프, 루프 당 3 : 7.05 μs 중 최고
  • ba) 100000 루프, 루프 당 3 : 3μs 최고

더 긴 입력으로 :

  • ab) 100000 루프, 루프 당 최대 3 : 7.71 μs
  • ba) 100000 루프, 루프 당 3 : 6 : 08 μs 중 최고

ba가독성과 속도 를 위해 사용하겠습니다 .

추가

주석에서 haccks에 의해 프롬프트되며 , 확인 ab과 확인의 차이점은 하나 입니다. 두 가지 변형에 대해 테스트 해 보겠습니다.baif c in text:

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)

Python 2.7.14 및 3.6.3 및 이전 세트와 다른 시스템에서 루프 당 μs 시간은 직접 비교할 수 없습니다.

╭────────────╥──────┬───────────────┬──────┬──────────────────╮
 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          
└────────────╨──────┴───────────────┴──────┴──────────────────┘

우리는 결론을 내릴 수 있습니다.

  • 수표가있는 사람은 수표가없는 사람보다 최대 4 배 빠릅니다.

  • ab_with_check파이썬 3에서는 약간 우위에 있지만, ba체크하면 파이썬 2에서는 우위에 있습니다.

  • 그러나 여기서 가장 큰 교훈은 Python 3이 Python 2보다 최대 3 배 빠릅니다 . 가장 느린 Python 3과 Python 2에서 가장 큰 차이는 없습니다!


4
이것이 왜 예외가 아닌가?
치킨 수프

if c in text:필요 ba합니까?
haccks

@haccks 필요하지는 않지만 2-3 배 빠릅니다. 짧은와 문자열 : 1.45 usec per loop: 그리고없이 5.3 usec per loop함께 긴 문자열 : 4.38 usec per loop및없이 : 7.03 usec per loop. (이것은 다른 기계이기 때문에 위의 결과와 직접 비교할 수는 없습니다.)
Hugo

1
@ 휴고; 이 시간 차이 는에서 반복 될 때마다 호출되는 경우 replace에만 호출 되기 때문이라고 생각합니다 . ctextbaab
haccks

2
@haccks 감사합니다. 추가 타이밍으로 답변을 업데이트했습니다. 확인을 추가하는 것이 두 가지 모두에 더 좋지만 가장 큰 교훈은 Python 3이 최대 3 배 빠릅니다!
Hugo Hugo

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

이중 백 슬래시가 필요한 이유는 무엇입니까? 왜 "\"가 작동하지 않습니까?
axolotl

3
이중 백 슬래시는 백 슬래시를 이스케이프합니다. 그렇지 않으면 파이썬은 "\"를 여전히 열려있는 문자열 내에서 리터럴 인용 문자로 해석합니다.
Riet

왜해야 string=string.replace(ch,"\\"+ch)합니까? 단지가 string.replace(ch,"\\"+ch)충분?
MattSom

1
@MattSom replace ()는 원래 문자열을 수정하지 않지만 복사본을 반환합니다. 따라서 코드에 영향을 주려면 할당이 필요합니다.
벤 브라이언

3
정말로 if가 필요합니까? 어쨌든 교체가 수행 할 작업의 중복처럼 보입니다.
lorenzo

32

replace이와 같은 기능을 간단히 연결

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

교체품이 더 많아 질 경우, 일반적인 방법으로이 작업을 수행 할 수 있습니다

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

30

다음은 str.translateand를 사용하는 python3 방법입니다 str.maketrans.

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

인쇄 된 문자열은 abc\&def\#ghi입니다.


2
이것은 좋은 대답이지만 실제로는 하나를 수행하는 .translate()것이 세 개의 체인 .replace()(CPython 3.6.4 사용) 보다 느립니다 .
Changaco

@Changaco 타이밍 주셔서 감사합니다 👍 실제로 나는 replace()나 자신 을 사용 하지만 완전성을 위해이 답변을 추가했습니다.
tommy.carstensen

큰 줄과 많은 대체품의 경우 좀 더 빠르지 만 일부 테스트는 좋을 것입니다 ...
Graipher

글쎄, 그것은 내 컴퓨터에 있지 않습니다 (2 및 17 교체와 동일).
Graipher

어떻게 '\#'유효합니까? 그것은 안 r'\#''\\#'? 아마도 코드 블록 형식 문제 일 수 있습니다.
parity3

16

항상 백 슬래시를 추가 하시겠습니까? 그렇다면 시도하십시오

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

가장 효율적인 방법은 아니지만 가장 쉬운 방법이라고 생각합니다.


15
aarrgghh tryr'\\\1'
John Machin

10

파티에 늦었지만 답변을 찾을 때 까지이 문제로 많은 시간을 잃었습니다.

짧고 달콤한, translate우월합니다replace . 시간 최적화에 따른 기능에 더 관심이있는 경우을 사용하지 마십시오 replace.

또한 사용하는 translate문자 집합을 대체하는 데 사용되는 문자 집합 중복 교체 할 경우 당신이 모르는 경우.

지목 사항:

를 사용 replace하면 코드 조각이 순진하게 "1234".replace("1", "2").replace("2", "3").replace("3", "4")반환 "2344"되지만 실제로 반환됩니다 "4444".

번역은 원래 OP가 원하는 것을 수행하는 것으로 보입니다.


6

일반적인 이스케이프 함수 작성을 고려할 수 있습니다.

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

이 방법으로 이스케이프해야하는 문자 목록을 사용하여 기능을 구성 할 수 있습니다.


3

참고로, OP에는 거의 사용되지 않지만 다른 독자에게는 유용 할 수 있습니다 (공감하지 마십시오. 알고 있습니다).

다소 어리석지 만 흥미로운 연습으로, 파이썬 함수 프로그래밍을 사용하여 여러 문자를 바꿀 수 있는지 확인하고 싶었습니다. 나는 이것이 replace ()를 두 번 호출하는 것보다 이길 수 없다고 확신합니다. 그리고 성능이 문제라면 녹, C, 줄리아, 펄, 자바, 자바 스크립트 및 어색한 부분에서 쉽게 이길 수 있습니다. Cython을 통해 가속화 된 pytoolz 라는 외부 '도우미'패키지를 사용합니다 ( cytoolz, 그것은 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),))))))

아무도 이것을 사용하여 여러 번 바꾸기를 귀찮게하지 않기 때문에 이것을 설명조차하지 않을 것입니다. 그럼에도 불구하고, 나는이 일을 어느 정도 성취했다고 느꼈고 다른 독자들에게 영감을 주거나 코드 난독 화 대회에서 이길 수 있다고 생각했습니다.


1
"함수 프로그래밍"은 "가능한 많은 기능을 사용"한다는 의미는 아닙니다.
Craig Andrews

1
이것은 완벽하게 좋은 순수한 기능 멀티 문자의 대체물이다 gist.github.com/anonymous/4577424f586173fc6b91a215ea2ce89e 없음 할당, 아니 돌연변이, 부작용. 읽을 수 있습니다.
Craig Andrews

1

python2.7 및 python3. *에서 사용 가능한 reduce를 사용하면 깨끗하고 파이썬적인 방식으로 여러 하위 문자열을 쉽게 바꿀 수 있습니다.

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

python2.7에서는 reduce를 가져올 필요가 없지만 python3. *에서는 functools 모듈에서 가져와야합니다.



1

이건 어때요?

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

그때

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

산출

\&\#

답변 과 유사


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

백 슬래시를 특수하게 처리하지 않는 원시 문자열이기 때문에 '원시'문자열 (대체 문자열 앞에 'r'로 표시)을 사용하려고합니다.


0

정규식을 사용하는 고급 방법

import re
text = "hello ,world!"
replaces = {"hello": "hi", "world":" 2020", "!":"."}
regex = re.sub("|".join(replaces.keys()), lambda match: replaces[match.string[match.start():match.end()]], text)
print(regex)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.