De beste manier om meerdere tekens in een tekenreeks te vervangen?

Ik moet een aantal tekens als volgt vervangen: &\&, #\#, …

Ik heb als volgt gecodeerd, maar ik denk dat er een betere manier moet zijn. Enige hints?

strs = strs.replace('&', '\&')
strs = strs.replace('#', '\#')
...

Antwoord 1, autoriteit 100%

Twee tekens vervangen

Ik heb alle methoden in de huidige antwoorden getimed, samen met één extra.

Met een invoerreeks van abc&def#ghien ter vervanging van & -> \& en # -> \#, de snelste manier was om de vervangingen als volgt aan elkaar te koppelen: text.replace('&', '\&').replace('#', '\#').

Timingen voor elke functie:

  • a) 1000000 lussen, het beste van 3: 1,47 μs per lus
  • b) 1000000 lussen, het beste van 3: 1,51 μs per lus
  • c) 100000 lussen, beste van 3: 12,3 μs per lus
  • d) 100000 lussen, beste van 3: 12 μs per lus
  • e) 100000 lussen, beste van 3: 3,27 μs per lus
  • f) 1000000 lussen, beste van 3: 0,817 μs per lus
  • g) 100000 lussen, beste van 3: 3,64 μs per lus
  • h) 1000000 lussen, beste van 3: 0,927 μs per lus
  • i) 1000000 lussen, beste van 3: 0,814 μs per lus

Dit zijn de functies:

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

Zo getimed:

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 tekens vervangen

Hier is een vergelijkbare code om hetzelfde te doen, maar met meer tekens om te ontsnappen (\`*_{}>#+-.!$):

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

Hier zijn de resultaten voor dezelfde invoerreeks abc&def#ghi:

  • a) 100000 lussen, beste van 3: 6.72 μs per lus
  • b) 100000 lussen, beste van 3: 2,64 μs per lus
  • c) 100000 lussen, beste van 3: 11,9 μs per lus
  • d) 100000 lussen, beste van 3: 4,92 μs per lus
  • e) 100000 lussen, beste van 3: 2,96 μs per lus
  • f) 100000 lussen, beste van 3: 4,29 μs per lus
  • g) 100000 lussen, beste van 3: 4,68 μs per lus
  • H) 100000 lussen, beste van 3: 4,73 μs per lus
  • I) 100000 lussen, beste van 3: 4,24 μs per lus

en met een langere invoerreeks (## *Something* and [another] thing in a longer sentence with {more} things to replace$) te vervangen:

  • a) 100000 lussen, beste van 3: 7,59 μs per lus
  • b) 100000 lussen, beste van 3: 6.54 μs per lus
  • c) 100000 lussen, beste van 3: 16,9 μs per lus
  • d) 100000 lussen, beste van 3: 7,29 μs per lus
  • e) 100000 lussen, beste van 3: 12.2 μs per lus
  • f) 100000 lussen, beste van 3: 5,38 μs per lus
  • g) 10000 lussen, beste van 3: 21,7 μs per lus
  • H) 100000 lussen, beste van 3: 5,7 μs per lus
  • I) 100000 lussen, beste van 3: 5.13 μs per lus

Een paar varianten toevoegen:

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)

Met de kortere ingang:

  • ab) 100000 lussen, beste van 3: 7,05 μs per lus
  • ba) 100000 lussen, beste van 3: 2,4 μs per lus

Met de langere ingang:

  • ab) 100000 lussen, beste van 3: 7,71 μs per lus
  • ba) 100000 lussen, beste van 3: 6.08 μs per lus

Dus ik ga bavoor leesbaarheid en snelheid gebruiken.

Addendum

Gevraagd door Haccks in de opmerkingen, één verschil tussen aben bais de if c in text:Controle. Laten we ze testen tegen nog twee varianten:

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)

Tijden in μs per lus op Python 2.7.14 en 3.6.3, en op een andere machine uit de eerdere set, dus kan niet rechtstreeks worden vergeleken.

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

We kunnen concluderen dat:

  • Degenen met de cheque zijn tot 4x sneller dan degenen zonder de cheque

  • ab_with_checkheeft een kleine voorsprong op Python 3, maar ba(met check) heeft een grotere voorsprong op Python 2

  • De grootste les hier is echter dat Python 3 tot 3x sneller is dan Python 2! Er is geen enorm verschil tussen de langzaamste op Python 3 en de snelste op Python 2!


Antwoord 2, autoriteit 13%

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

Antwoord 3, autoriteit 9%

Hier is een python3-methode die gebruikmaakt van str.translateen str.maketrans:

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

De afgedrukte tekenreeks is abc\&def\#ghi.


Antwoord 4, autoriteit 6%

Sluit de replace-functies gewoon zo aan

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

Als het aantal vervangingen groter zal zijn, kunt u dit op deze generieke manier doen

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

Antwoord 5, autoriteit 3%

Ga je altijd een backslash toevoegen? Zo ja, probeer dan

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

Het is misschien niet de meest efficiënte methode, maar ik denk dat het de gemakkelijkste is.


Antwoord 6, autoriteit 2%

Laat op het feest, maar ik verloor veel tijd met dit probleem totdat ik mijn antwoord vond.

Kort gezegd, translateis superieur aan replace. Als u meer geïnteresseerd bent in optimalisatie van functionaliteit in de loop van de tijd, gebruik dan geen replace.

Gebruik ook translateals u niet weet of de set tekens die moet worden vervangen de set tekens overlapt die worden gebruikt om te vervangen.

Voorbeeld:

Als je replacegebruikt, zou je naïef verwachten dat het fragment "1234".replace("1", "2").replace("2", "3").replace("3", "4")om "2344"te retourneren, maar het zal in feite "4444"retourneren.

Vertaling lijkt te doen wat OP oorspronkelijk wilde.


Antwoord 7

U kunt overwegen een algemene ontsnappingsfunctie te schrijven:

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

Op deze manier kunt u uw functie configureren met een lijst met teken die moet worden ontsnapt.


Antwoord 8

FYI, dit is van weinig of geen gebruik aan het OP, maar het kan van nut zijn voor andere lezers (gelieve geen downvote, ik ben hiervan niet op de hoogte).

Als een enigszins belachelijke maar interessante oefening, wilde zien of ik de Python-functionele programmering kon gebruiken om meerdere tekens te vervangen. Ik ben er vrij zeker van dat dit niet zojuist is verslaan, alleen maar vervangen () tweemaal. En als de uitvoering een probleem was, kunt u dit gemakkelijk verslaan in Rust, C, Julia, Perl, Java, JavaScript en misschien zelfs AWK. Het maakt gebruik van een extern ‘Helpers’ pakket genaamd pytoolz , versneld via Cython (Cytoolz, het is een PYPI-pakket ).

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

Ik ga dit niet eens uitleggen omdat niemand de moeite neemt om dit te gebruiken om meerdere vervangen te bereiken. Desalniettemin voelde ik me enigszins bereikt in het doen van dit en dacht dat het andere lezers zou kunnen inspireren of een code-obfuscation-wedstrijd wint.


Antwoord 9

Verklein gebruiken die beschikbaar is in Python2.7 en Python3. * U kunt eenvoudig mutiple-substrings op een schone en pythonische manier vervangen.

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

In python2.7 hoef je reduce niet te importeren, maar in python3.* moet je het importeren vanuit de functools-module.


Antwoord 10

Misschien een eenvoudige lus voor tekens om te vervangen:

a = '&#'
to_replace = ['&', '#']
for char in to_replace:
    a = a.replace(char, "\\"+char)
print(a)
>>> \&\#

Antwoord 11

Wat dacht je hiervan?

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

dan

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

uitvoer

\&\#

vergelijkbaar met answer


Antwoord 12

geavanceerde manier met regex

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)

Antwoord 13

Voor Python 3.8 en hoger kan men toewijzing uitdrukkingen

gebruiken

(text := text.replace(s, f"\\{i}") for s in "&#" if s in text)

Hoewel, ik ben helemaal niet zeker of dit als “passend gebruik” van opdrachtuitdrukkingen zou worden beschouwd zoals beschreven in PEP 572 , maar ziet er schoon uit en leest vrij goed (tegen mijn ogen). Dit zou “passend” zijn als u ook alle intermediaire snaren wilde. Bijvoorbeeld (verwijderen van alle klinkers van kleine letters):

text = "Lorem ipsum dolor sit amet"
intermediates = [text := text.replace(i, "") for i in "aeiou" if i in text]
['Lorem ipsum dolor sit met',
 'Lorm ipsum dolor sit mt',
 'Lorm psum dolor st mt',
 'Lrm psum dlr st mt',
 'Lrm psm dlr st mt']

Aan de positieve kant lijkt het (onverwachts?) Sneller dan sommige van de snellere methoden in het geaccepteerde antwoord, en lijkt mooi te presteren met beide toenemende snarengte en een toenemend aantal substituties.

De code voor de bovenstaande vergelijking is hieronder. Ik gebruik willekeurige snaren om mijn leven een beetje eenvoudiger te maken, en de te vervangen karakters worden willekeurig uit de string zelf gekozen. (Opmerking: ik gebruik hier IPYTHON’s% timeit-magie, dus laat dit in IPYTHON / JUPLYTER).

import random, string
def make_txt(length):
    "makes a random string of a given length"
    return "".join(random.choices(string.printable, k=length))
def get_substring(s, num):
    "gets a substring"
    return "".join(random.choices(s, k=num))
def a(text, replace): # one of the better performing approaches from the accepted answer
    for i in replace:
        if i in text:
             text = text.replace(i, "")
def b(text, replace):
    _ = (text := text.replace(i, "") for i in replace if i in text) 
def compare(strlen, replace_length):
    "use ipython / jupyter for the %timeit functionality"
    times_a, times_b = [], []
    for i in range(*strlen):
        el = make_txt(i)
        et = get_substring(el, replace_length)
        res_a = %timeit -n 1000 -o a(el, et) # ipython magic
        el = make_txt(i)
        et = get_substring(el, replace_length)
        res_b = %timeit -n 1000 -o b(el, et) # ipython magic
        times_a.append(res_a.average * 1e6)
        times_b.append(res_b.average * 1e6)
    return times_a, times_b
#----run
t2 = compare((2*2, 1000, 50), 2)
t10 = compare((2*10, 1000, 50), 10)

Antwoord 14

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

U wilt een ‘onbewerkte’ tekenreeks gebruiken (aangeduid met de ‘r’ die voorafgaat aan de vervangende tekenreeks), aangezien onbewerkte tekenreeksen de backslash niet speciaal behandelen.


Antwoord 15

Dit zal iemand helpen die op zoek is naar een eenvoudige oplossing.

def replacemany(our_str, to_be_replaced:tuple, replace_with:str):
    for nextchar in to_be_replaced:
        our_str = our_str.replace(nextchar, replace_with)
    return our_str
os = 'the rain in spain falls mainly on the plain ttttttttt sssssssssss nnnnnnnnnn'
tbr = ('a','t','s','n')
rw = ''
print(replacemany(os,tbr,rw))

Uitvoer:

he ri i pi fll mily o he pli

Other episodes