wake-up-neo.net

Massensaite in Python ersetzen?

Angenommen, ich habe eine Zeichenfolge, die folgendermaßen aussieht:

str = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog"

Sie werden viele Stellen in der Zeichenfolge bemerken, an denen ein kaufmännisches Und steht, gefolgt von einem Zeichen (wie "& y" und "& c"). Ich muss diese Zeichen durch einen entsprechenden Wert ersetzen, den ich in einem Wörterbuch habe.

dict = {"&y":"\033[0;30m",
        "&c":"\033[0;31m",
        "&b":"\033[0;32m",
        "&Y":"\033[0;33m",
        "&u":"\033[0;34m"}

Wie geht das am schnellsten? Ich könnte manuell alle kaufmännischen Und-Zeichen finden und dann das Wörterbuch durchlaufen, um sie zu ändern, aber das scheint langsam zu sein. Eine Reihe von Regex-Ersetzungen durchzuführen, scheint ebenfalls langsam zu sein (ich werde ein Wörterbuch mit etwa 30-40 Paaren in meinem aktuellen Code haben).

Anregungen sind dankbar, danke.

Bearbeiten:

Wie in den Kommentaren zu dieser Frage erwähnt, wird mein Wörterbuch vor der Laufzeit definiert und wird sich im Laufe des Anwendungslebenszyklus nie ändern. Es ist eine Liste von ANSI-Escape-Sequenzen und enthält ungefähr 40 Elemente. Die durchschnittliche Länge der zu vergleichenden Zeichenfolge beträgt ungefähr 500 Zeichen, es gibt jedoch Zeichen mit bis zu 5000 Zeichen (obwohl dies selten vorkommt). Ich benutze derzeit auch Python 2.6.

Edit # 2 Ich akzeptierte Tor Valamos Antwort als die richtige, da es nicht nur eine gültige Lösung gab (obwohl es nicht die beste Lösung war), sondern alle anderen berücksichtigte und tat eine enorme Menge an Arbeit, um sie alle zu vergleichen. Diese Antwort ist eine der besten und hilfreichsten Antworten, die ich je auf StackOverflow erhalten habe. Hut ab.

42
Mike Trpcic
mydict = {"&y":"\033[0;30m",
          "&c":"\033[0;31m",
          "&b":"\033[0;32m",
          "&Y":"\033[0;33m",
          "&u":"\033[0;34m"}
mystr = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog"

for k, v in mydict.iteritems():
    mystr = mystr.replace(k, v)

print mystr
The ←[0;30mquick ←[0;31mbrown ←[0;32mfox ←[0;33mjumps over the ←[0;34mlazy dog

Ich habe mir erlaubt, einige Lösungen zu vergleichen:

mydict = dict([('&' + chr(i), str(i)) for i in list(range(65, 91)) + list(range(97, 123))])

# random inserts between keys
from random import randint
rawstr = ''.join(mydict.keys())
mystr = ''
for i in range(0, len(rawstr), 2):
    mystr += chr(randint(65,91)) * randint(0,20) # insert between 0 and 20 chars

from time import time

# How many times to run each solution
rep = 10000

print 'Running %d times with string length %d and ' \
      'random inserts of lengths 0-20' % (rep, len(mystr))

# My solution
t = time()
for x in range(rep):
    for k, v in mydict.items():
        mystr.replace(k, v)
    #print(mystr)
print '%-30s' % 'Tor fixed & variable dict', time()-t

from re import sub, compile, escape

# Peter Hansen
t = time()
for x in range(rep):
    sub(r'(&[a-zA-Z])', r'%(\1)s', mystr) % mydict
print '%-30s' % 'Peter fixed & variable dict', time()-t

# Claudiu
def multiple_replace(dict, text): 
    # Create a regular expression  from the dictionary keys
    regex = compile("(%s)" % "|".join(map(escape, dict.keys())))

    # For each match, look-up corresponding value in dictionary
    return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)

t = time()
for x in range(rep):
    multiple_replace(mydict, mystr)
print '%-30s' % 'Claudio variable dict', time()-t

# Claudiu - Precompiled
regex = compile("(%s)" % "|".join(map(escape, mydict.keys())))

t = time()
for x in range(rep):
    regex.sub(lambda mo: mydict[mo.string[mo.start():mo.end()]], mystr)
print '%-30s' % 'Claudio fixed dict', time()-t

# Andrew Y - variable dict
def mysubst(somestr, somedict):
  subs = somestr.split("&")
  return subs[0] + "".join(map(lambda arg: somedict["&" + arg[0:1]] + arg[1:], subs[1:]))

t = time()
for x in range(rep):
    mysubst(mystr, mydict)
print '%-30s' % 'Andrew Y variable dict', time()-t

# Andrew Y - fixed
def repl(s):
  return mydict["&"+s[0:1]] + s[1:]

t = time()
for x in range(rep):
    subs = mystr.split("&")
    res = subs[0] + "".join(map(repl, subs[1:]))
print '%-30s' % 'Andrew Y fixed dict', time()-t

Ergebnisse in Python 2.6

Running 10000 times with string length 490 and random inserts of lengths 0-20
Tor fixed & variable dict      1.04699993134
Peter fixed & variable dict    0.218999862671
Claudio variable dict          2.48400020599
Claudio fixed dict             0.0940001010895
Andrew Y variable dict         0.0309998989105
Andrew Y fixed dict            0.0310001373291

Die Lösungen von claudiu und andrew gingen immer weiter auf 0, sodass ich sie auf 10.000 Läufe erhöhen musste.

Ich habe es in Python 3 (wegen Unicode) ausgeführt, wobei Zeichen von 39 bis 1024 ersetzt wurden (38 ist ein kaufmännisches Und, deshalb wollte ich es nicht einschließen). Stringlänge bis zu 10.000, einschließlich etwa 980 Ersetzungen mit variablen Zufallseinsätzen der Länge 0-20. Die Unicode-Werte von 39 bis 1024 verursachen Zeichen mit einer Länge von 1 und 2 Bytes, die sich auf einige Lösungen auswirken können.

mydict = dict([('&' + chr(i), str(i)) for i in range(39,1024)])

# random inserts between keys
from random import randint
rawstr = ''.join(mydict.keys())
mystr = ''
for i in range(0, len(rawstr), 2):
    mystr += chr(randint(65,91)) * randint(0,20) # insert between 0 and 20 chars

from time import time

# How many times to run each solution
rep = 10000

print('Running %d times with string length %d and ' \
      'random inserts of lengths 0-20' % (rep, len(mystr)))

# Tor Valamo - too long
#t = time()
#for x in range(rep):
#    for k, v in mydict.items():
#        mystr.replace(k, v)
#print('%-30s' % 'Tor fixed & variable dict', time()-t)

from re import sub, compile, escape

# Peter Hansen
t = time()
for x in range(rep):
    sub(r'(&[a-zA-Z])', r'%(\1)s', mystr) % mydict
print('%-30s' % 'Peter fixed & variable dict', time()-t)

# Peter 2
def dictsub(m):
    return mydict[m.group()]

t = time()
for x in range(rep):
    sub(r'(&[a-zA-Z])', dictsub, mystr)
print('%-30s' % 'Peter fixed dict', time()-t)

# Claudiu - too long
#def multiple_replace(dict, text): 
#    # Create a regular expression  from the dictionary keys
#    regex = compile("(%s)" % "|".join(map(escape, dict.keys())))
#
#    # For each match, look-up corresponding value in dictionary
#    return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
#
#t = time()
#for x in range(rep):
#    multiple_replace(mydict, mystr)
#print('%-30s' % 'Claudio variable dict', time()-t)

# Claudiu - Precompiled
regex = compile("(%s)" % "|".join(map(escape, mydict.keys())))

t = time()
for x in range(rep):
    regex.sub(lambda mo: mydict[mo.string[mo.start():mo.end()]], mystr)
print('%-30s' % 'Claudio fixed dict', time()-t)

# Separate setup for Andrew and gnibbler optimized dict
mydict = dict((k[1], v) for k, v in mydict.items())

# Andrew Y - variable dict
def mysubst(somestr, somedict):
  subs = somestr.split("&")
  return subs[0] + "".join(map(lambda arg: somedict[arg[0:1]] + arg[1:], subs[1:]))

def mysubst2(somestr, somedict):
  subs = somestr.split("&")
  return subs[0].join(map(lambda arg: somedict[arg[0:1]] + arg[1:], subs[1:]))

t = time()
for x in range(rep):
    mysubst(mystr, mydict)
print('%-30s' % 'Andrew Y variable dict', time()-t)
t = time()
for x in range(rep):
    mysubst2(mystr, mydict)
print('%-30s' % 'Andrew Y variable dict 2', time()-t)

# Andrew Y - fixed
def repl(s):
  return mydict[s[0:1]] + s[1:]

t = time()
for x in range(rep):
    subs = mystr.split("&")
    res = subs[0] + "".join(map(repl, subs[1:]))
print('%-30s' % 'Andrew Y fixed dict', time()-t)

# gnibbler
t = time()
for x in range(rep):
    myparts = mystr.split("&")
    myparts[1:]=[mydict[x[0]]+x[1:] for x in myparts[1:]]
    "".join(myparts)
print('%-30s' % 'gnibbler fixed & variable dict', time()-t)

Ergebnisse:

Running 10000 times with string length 9491 and random inserts of lengths 0-20
Tor fixed & variable dict      0.0 # disqualified 329 secs
Peter fixed & variable dict    2.07799983025
Peter fixed dict               1.53100013733 
Claudio variable dict          0.0 # disqualified, 37 secs
Claudio fixed dict             1.5
Andrew Y variable dict         0.578000068665
Andrew Y variable dict 2       0.56299996376
Andrew Y fixed dict            0.56200003624
gnibbler fixed & variable dict 0.530999898911

(** Beachten Sie, dass der Code von gnibbler ein anderes Diktat verwendet, wobei für die Schlüssel kein '&' enthalten ist. Andrews Code verwendet auch dieses alternative Diktat, aber es hat keinen großen Unterschied gemacht, vielleicht nur das 0,01-fache.

30
Tor Valamo

Versuchen Sie dies, indem Sie die Ersetzung regulärer Ausdrücke und die Standardzeichenfolgenformatierung verwenden:

# using your stated values for str and dict:
>>> import re
>>> str = re.sub(r'(&[a-zA-Z])', r'%(\1)s', str)
>>> str % dict
'The \x1b[0;30mquick \x1b[0;31mbrown \x1b[0;32mfox \x1b[0;33mjumps over the \x1b[0;34mlazy dog'

Der Aufruf von resub () ersetzt alle Sequenzen des kaufmännischen Und-Zeichens, gefolgt von einem einzelnen Buchstaben, wobei das Muster% (..) das gleiche Muster enthält.

Die% -Formatierung nutzt eine Funktion der String-Formatierung, bei der ein Wörterbuch anstelle der häufiger vorkommenden Positionsargumente die Angabe der Ersetzung erfordert.

Eine Alternative kann dies direkt in der resub mit einem Rückruf tun:

>>> import re
>>> def dictsub(m):
>>>    return dict[m.group()]
>>> str = re.sub(r'(&[a-zA-Z])', dictsub, str)

Dieses Mal verwende ich eine Schließung, um das Wörterbuch aus der Callback-Funktion heraus zu referenzieren. Dieser Ansatz könnte Ihnen etwas mehr Flexibilität geben. Sie könnten beispielsweise etwas wie dict.get(m.group(), '??') verwenden, um das Auftreten von Ausnahmen zu vermeiden, wenn Sie Zeichenfolgen mit nicht erkannten Codefolgen hatten.

(Übrigens sind sowohl "dict" als auch "str" ​​eingebaute Funktionen, und Sie werden in Schwierigkeiten geraten, wenn Sie diese Namen in Ihrem eigenen Code verwenden. Nur für den Fall, dass Sie das nicht wussten eine Frage wie diese natürlich.)

Edit: Ich entschied mich, Tor's Testcode zu überprüfen, und kam zu dem Schluss, dass er bei weitem nicht repräsentativ und in der Tat fehlerhaft ist. Die generierte Zeichenfolge enthält nicht einmal ein kaufmännisches Und (!). Der folgende überarbeitete Code generiert ein repräsentatives Wörterbuch und eine Zeichenfolge, ähnlich den Beispieleingaben des OP.

Ich wollte auch überprüfen, ob die Ausgabe jedes Algorithmus gleich ist. Nachfolgend finden Sie ein überarbeitetes Testprogramm, das nur Tor's, Mine und Claudius Code enthält - weil die anderen bei der Probeneingabe gebrochen haben. (Ich denke, sie sind alle brüchig, es sei denn, das Wörterbuch bildet im Wesentlichen all mögliche kaufmännische Und-Sequenzen ab, die Tors Testcode ausführte.) Dieser Befehl enthält den Zufallszahlengenerator, sodass jeder Lauf derselbe ist. Schließlich habe ich eine kleinere Variante mit einem Generator hinzugefügt, der einige Funktionsaufrufe für eine geringfügige Leistungsverbesserung vermeidet.

from time import time
import string
import random
import re

random.seed(1919096)  # ensure consistent runs

# build dictionary with 40 mappings, representative of original question
mydict = dict(('&' + random.choice(string.letters), '\x1b[0;%sm' % (30+i)) for i in range(40))
# build simulated input, with mix of text, spaces, ampersands in reasonable proportions
letters = string.letters + ' ' * 12 + '&' * 6
mystr = ''.join(random.choice(letters) for i in range(1000))

# How many times to run each solution
rep = 10000

print('Running %d times with string length %d and %d ampersands'
    % (rep, len(mystr), mystr.count('&')))

# Tor Valamo
# fixed from Tor's test, so it actually builds up the final string properly
t = time()
for x in range(rep):
    output = mystr
    for k, v in mydict.items():
        output = output.replace(k, v)
print('%-30s' % 'Tor fixed & variable dict', time() - t)
# capture "known good" output as expected, to verify others
expected = output

# Peter Hansen

# build charset to use in regex for safe dict lookup
charset = ''.join(x[1] for x in mydict.keys())
# grab reference to method on regex, for speed
patsub = re.compile(r'(&[%s])' % charset).sub

t = time()
for x in range(rep):
    output = patsub(r'%(\1)s', mystr) % mydict
print('%-30s' % 'Peter fixed & variable dict', time()-t)
assert output == expected

# Peter 2
def dictsub(m):
    return mydict[m.group()]

t = time()
for x in range(rep):
    output = patsub(dictsub, mystr)
print('%-30s' % 'Peter fixed dict', time() - t)
assert output == expected

# Peter 3 - freaky generator version, to avoid function call overhead
def dictsub(d):
    m = yield None
    while 1:
        m = yield d[m.group()]

dictsub = dictsub(mydict).send
dictsub(None)   # "prime" it
t = time()
for x in range(rep):
    output = patsub(dictsub, mystr)
print('%-30s' % 'Peter generator', time() - t)
assert output == expected

# Claudiu - Precompiled
regex_sub = re.compile("(%s)" % "|".join(mydict.keys())).sub

t = time()
for x in range(rep):
    output = regex_sub(lambda mo: mydict[mo.string[mo.start():mo.end()]], mystr)
print('%-30s' % 'Claudio fixed dict', time() - t)
assert output == expected

Ich habe vergessen, Benchmark-Ergebnisse vorzulegen:

 Läuft 10000 Mal mit der Stringlänge 1000 und 96 und ... 
 ('Tor fixed & variable dict', 2.9890000820159912) 
 ('Peter fixed & variable dict', 2.6659998893737793) 
 ('Peter fixed dict', 1.0920000076293945) 
 ('Peter generator', 1.0460000038146973) 
 ('Claudio fixed dict', 1.562000036239624) 

Schnipsel der Eingänge und korrekte Ausgabe:

mystr = 'lTEQDMAPvksk k&z Txp vrnhQ GHaO&GNFY&&a...'
mydict = {'&p': '\x1b[0;37m', '&q': '\x1b[0;66m', '&v': ...}
output = 'lTEQDMAPvksk k←[0;57m Txp vrnhQ GHaO←[0;67mNFY&&a P...'

Verglichen mit dem, was ich von Tor's Testcode-Ausgabe gesehen habe:

mystr = 'VVVVVVVPPPPPPPPPPPPPPPXXXXXXXXYYYFFFFFFFFFFFFEEEEEEEEEEE...'
mydict = {'&p': '112', '&q': '113', '&r': '114', '&s': '115', ...}
output = # same as mystr since there were no ampersands inside
14
Peter Hansen

Wenn Sie wirklich in das Thema eintauchen möchten, schauen Sie sich dies an: http://en.wikipedia.org/wiki/Aho-Corasick_algorithm

Die naheliegende Lösung durch Iterieren über das Wörterbuch und Ersetzen jedes Elements in der Zeichenfolge erfordert O(n*m) Zeit, wobei n die Größe des Wörterbuchs und m die Länge der Zeichenfolge ist.

Der Aho-Corasick-Algorithmus findet alle Einträge des Wörterbuchs in O(n+m+f), wobei f die Anzahl der gefundenen Elemente ist.

8
WolfgangP

Wenn die Anzahl der Schlüssel in der Liste groß ist und die Anzahl der Vorkommen in der Zeichenfolge niedrig ist (und meistens null), können Sie die Vorkommen der Et-Zeichen in der Zeichenfolge durchlaufen und das durch den ersten Schlüssel eingegebene Wörterbuch verwenden Zeichen der Teilzeichenfolgen. Ich programmiere nicht oft in Python, daher ist der Stil vielleicht etwas abgelehnt, aber hier ist meine Meinung dazu:

str = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog"

dict = {"&y":"\033[0;30m",
        "&c":"\033[0;31m",
        "&b":"\033[0;32m",
        "&Y":"\033[0;33m",
        "&u":"\033[0;34m"}

def rep(s):
  return dict["&"+s[0:1]] + s[1:]

subs = str.split("&")
res = subs[0] + "".join(map(rep, subs[1:]))

print res

Natürlich gibt es eine Frage, was passiert, wenn ein Et-Zeichen von der Zeichenfolge selbst kommt. Sie müssen es irgendwie entziehen, bevor Sie diesen Prozess durchlaufen, und dann nach diesem Prozess die Flucht beenden. 

Wie bei den Leistungsproblemen üblich, ist es natürlich eine gute Sache, die verschiedenen Ansätze für Ihren typischen (und auch den schlimmsten Fall) Datensatz zu bestimmen und zu vergleichen.

BEARBEITEN: Platziere es in einer separaten Funktion, um mit einem beliebigen Wörterbuch zu arbeiten:

def mysubst(somestr, somedict):
  subs = somestr.split("&")
  return subs[0] + "".join(map(lambda arg: somedict["&" + arg[0:1]] + arg[1:], subs[1:]))

EDIT2: eine unnötige Verkettung loswerden, scheint bei vielen Iterationen immer noch etwas schneller als die vorherigen zu sein.

def mysubst(somestr, somedict):
  subs = somestr.split("&")
  return subs[0].join(map(lambda arg: somedict["&" + arg[0:1]] + arg[1:], subs[1:]))
6
Andrew Y

Hier ist der Ansatz der C-Erweiterungen für Python

const char *dvals[]={
    //"0-64
    "","","","","","","","","","",
    "","","","","","","","","","",
    "","","","","","","","","","",
    "","","","","","","","","","",
    "","","","","","","","","","",
    "","","","","","","","","","",
    "","","","","",
    //A-Z
    "","","","","",
    "","","","","",
    "","","","","",
    "","","","","",
    "","","","","33",
    "",
    //
    "","","","","","",
    //a-z
    "","32","31","","",
    "","","","","",
    "","","","","",
    "","","","","",
    "34","","","","30",
    ""
};

int dsub(char*d,char*s){
    char *ofs=d;
    do{
        if(*s=='&' && s[1]<='z' && *dvals[s[1]]){

            //\033[0;
            *d++='\\',*d++='0',*d++='3',*d++='3',*d++='[',*d++='0',*d++=';';

            //consider as fixed 2 digits
            *d++=dvals[s[1]][0];
            *d++=dvals[s[1]][1];

            *d++='m';

            s++; //skip

        //non &,invalid, unused (&) ampersand sequences will go here.
        }else *d++=*s;

    }while(*s++);

    return d-ofs-1;
}

Python-Codes, die ich getestet habe

from mylib import *
import time

start=time.time()

instr="The &yquick &cbrown &bfox &Yjumps over the &ulazy dog, skip &Unknown.\n"*100000
x=dsub(instr)

end=time.time()

print "time taken",end-start,",input str length",len(x)
print "first few lines"
print x[:1100]

Ergebnisse

time taken 0.140000104904 ,input str length 11000000
first few lines
The \033[0;30mquick \033[0;31mbrown \033[0;32mfox \033[0;33mjumps over the \033[0;34mlazy dog, skip &Unknown.
The \033[0;30mquick \033[0;31mbrown \033[0;32mfox \033[0;33mjumps over the \033[0;34mlazy dog, skip &Unknown.
The \033[0;30mquick \033[0;31mbrown \033[0;32mfox \033[0;33mjumps over the \033[0;34mlazy dog, skip &Unknown.
The \033[0;30mquick \033[0;31mbrown \033[0;32mfox \033[0;33mjumps over the \033[0;34mlazy dog, skip &Unknown.
The \033[0;30mquick \033[0;31mbrown \033[0;32mfox \033[0;33mjumps over the \033[0;34mlazy dog, skip &Unknown.
The \033[0;30mquick \033[0;31mbrown \033[0;32mfox \033[0;33mjumps over the \033[0;34mlazy dog, skip &Unknown.
The \033[0;30mquick \033[0;31mbrown \033[0;32mfox \033[0;33mjumps over the \033[0;34mlazy dog, skip &Unknown.
The \033[0;30mquick \033[0;31mbrown \033[0;32mfox \033[0;33mjumps over the \033[0;34mlazy dog, skip &Unknown.
The \033[0;30mquick \033[0;31mbrown \033[0;32mfox \033[0;33mjumps over the \033[0;34mlazy dog, skip &Unknown.
The \033[0;30mquick \033[0;31mbrown \033[0;32mfox \033[0;33mjumps over the \033[0;34mlazy dog, skip &Unknown.

Es wird angenommen, dass es möglich ist, mit O(n) zu laufen, und . Nur dauerte 160 ms (avg) für 11 MB string in meinem Mobile Celeron 1,6 GHz PC

Es werden auch unbekannte Zeichen übersprungen, wie es ist, zum Beispiel wird &Unknown so zurückgegeben, wie es ist

Sag mir Bescheid, falls du Probleme mit dem Kompilieren, Bugs usw. hast ...

4
YOU

Dieses scheint das zu tun, was Sie wollen - mehrere Zeichenfolgen werden mit RegExps gleichzeitig ersetzt. Hier ist der entsprechende Code:

def multiple_replace(dict, text): 
    # Create a regular expression  from the dictionary keys
    regex = re.compile("(%s)" % "|".join(map(re.escape, dict.keys())))

    # For each match, look-up corresponding value in dictionary
    return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)

print multiple_replace(dict, str)
3
Claudiu

Eine allgemeine Lösung zum Definieren von Ersetzungsregeln ist die Verwendung der Regex-Ersetzung mithilfe einer Funktion zum Bereitstellen der Karte (siehe re.sub () ).

import re

str = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog"

dict = {"&y":"\033[0;30m",
        "&c":"\033[0;31m",
        "&b":"\033[0;32m",
        "&Y":"\033[0;33m",
        "&u":"\033[0;34m"}

def programmaticReplacement( match ):
    return dict[ match.group( 1 ) ]

colorstring = re.sub( '(\&.)', programmaticReplacement, str )

Dies ist besonders für nicht-triviale Substitutionen (z. B. alles, was mathematische Operationen erfordert, um den Substitut zu erzeugen) Nizza.

3
charstar

Hier ist eine Version mit split/join

mydict = {"y":"\033[0;30m",
          "c":"\033[0;31m",
          "b":"\033[0;32m",
          "Y":"\033[0;33m",
          "u":"\033[0;34m"}
mystr = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog"

myparts = mystr.split("&")
myparts[1:]=[mydict[x[0]]+x[1:] for x in myparts[1:]]
print "".join(myparts)

Falls es Et-Zeichen mit ungültigen Codes gibt, können Sie diese verwenden, um sie zu erhalten

myparts[1:]=[mydict.get(x[0],"&"+x[0])+x[1:] for x in myparts[1:]]

Peter Hansen wies darauf hin, dass dies fehlschlägt, wenn es ein doppeltes Et-Zeichen gibt. In diesem Fall verwenden Sie diese Version

mystr = "The &yquick &cbrown &bfox &Yjumps over the &&ulazy dog"
myparts = mystr.split("&")
myparts[1:]=[mydict.get(x[:1],"&"+x[:1])+x[1:] for x in myparts[1:]]
print "".join(myparts)
3
John La Rooy

Da jemand einen einfachen Parser erwähnte, dachte ich, ich würde ihn mit Pyparsing zubereiten. Durch Verwendung der transformString-Methode von pyparsing wird intern die Quellzeichenfolge durchsucht und eine Liste des übereinstimmenden Textes und des dazwischenliegenden Textes erstellt. Wenn alles erledigt ist, transform'tring dann '' .join diese Liste, so dass es keine Leistungsprobleme beim Aufbau von Strings nach Inkrementen gibt. (Die für ANSIreplacer definierte Parsing-Aktion führt die Konvertierung von den übereinstimmenden & _-Zeichen in die gewünschte Escape-Sequenz durch und ersetzt den übereinstimmenden Text durch die Ausgabe der Parser-Aktion. Da nur übereinstimmende Sequenzen den Parser-Ausdruck erfüllen, ist die Verwendung der Aktion zum Analysieren von undefinierten & _-Sequenzen.) 

FollowedBy ('&') ist nicht unbedingt erforderlich, aber es führt zu einer Abkürzung des Analysevorgangs, indem überprüft wird, ob der Parser tatsächlich an einem Et-Zeichen positioniert ist, bevor alle Markup-Optionen teurer geprüft werden.

from pyparsing import FollowedBy, oneOf

escLookup = {"&y":"\033[0;30m",
            "&c":"\033[0;31m",
            "&b":"\033[0;32m",
            "&Y":"\033[0;33m",
            "&u":"\033[0;34m"}

# make a single expression that will look for a leading '&', then try to 
# match each of the escape expressions
ANSIreplacer = FollowedBy('&') + oneOf(escLookup.keys())

# add a parse action that will replace the matched text with the 
# corresponding ANSI sequence
ANSIreplacer.setParseAction(lambda toks: escLookup[toks[0]])

# now use the replacer to transform the test string; throw in some extra
# ampersands to show what happens with non-matching sequences
src = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog & &Zjumps back"
out = ANSIreplacer.transformString(src)
print repr(out)

Drucke:

'The \x1b[0;30mquick \x1b[0;31mbrown \x1b[0;32mfox \x1b[0;33mjumps over 
 the \x1b[0;34mlazy dog & &Zjumps back'

Dies wird sicherlich keine Leistungswettbewerbe gewinnen, aber wenn Ihr Markup komplizierter wird, ist die Erweiterung durch eine Parser-Foundation einfacher.

1
PaulMcG

Auch wenn Sie sich nicht sicher sind, wie schnell diese Lösung ist, könnten Sie einfach durch Ihr Wörterbuch blättern und wiederholt das eingebaute aufrufen 

str.replace(old, new) 

Dies kann anständig funktionieren, wenn die ursprüngliche Zeichenfolge nicht zu lang ist. Sie würde jedoch offensichtlich leiden, wenn die Zeichenfolge länger wird.

1

Das Problem bei der Massenersetzung in Python ist die Unveränderlichkeit der Zeichenfolgen: Jedes Mal, wenn Sie ein Element in der Zeichenfolge ersetzen, wird die gesamte neue Zeichenfolge immer wieder vom Heap aus neu zugewiesen.

Wenn Sie also die schnellste Lösung wünschen, müssen Sie entweder einen veränderbaren Container verwenden (z. B. Liste) oder diese Maschinerie in die Ebene C schreiben (oder besser in Pyrex oder Cython). In jedem Fall empfehle ich, einen einfachen Parser zu schreiben, der auf einer einfachen Finite-State-Maschine basiert, und die Symbole Ihrer Zeichenfolge nacheinander einzugeben.

Lösungsvorschläge basierend auf regulären Ausdrücken funktionieren auf ähnliche Weise, da reguläre Ausdrücke mit fsm hinter den Kulissen arbeiten.

1
bialix
>>> a=[]
>>> str = "The &yquick &cbrown &bfox &Yjumps over the &ulazy dog"
>>> d={"&y":"\033[0;30m",                                                              
... "&c":"\033[0;31m",                                                                 
... "&b":"\033[0;32m",                                                                 
... "&Y":"\033[0;33m",                                                                 
... "&u":"\033[0;34m"}     
>>> for item in str.split():
...  if item[:2] in d:
...    a.append(d[item[:2]]+item[2:])
...  else: a.append(item)
>>> print ' '.join(a)
0
ghostdog74

versuche dies

tr.replace ("& y", dikt ["& y"])

tr.replace ("& c", dikt ["& c"])

tr.replace ("& b", dict ["& b"])

tr.replace ("& Y", Dikt ["& Y"])

tr.replace ("& u", dikt ["& u"])

0
fahad