Массовая строка заменяется на python?

Скажем, у меня есть строка, которая выглядит так:

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

Вы заметите много мест в строке, где есть амперсанд, за которым следует символ (например, "& y" и "& c" ). Мне нужно заменить эти символы соответствующим значением, которое у меня есть в словаре, например:

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

Каков самый быстрый способ сделать это? Я мог бы вручную найти все амперсанды, а затем прокрутить словарь, чтобы изменить их, но это кажется медленным. Выполнение замены регулярных выражений кажется медленным (у меня будет словарь около 30-40 пар в моем фактическом коде).

Любые предложения приветствуются, спасибо.

Edit:

Как уже отмечалось в комментариях к этому вопросу, мой словарь определен до начала выполнения и никогда не будет меняться в течение жизненного цикла приложений. Это список escape-последовательностей ANSI и будет содержать около 40 элементов. Моя средняя длина строки для сравнения будет около 500 символов, но будут те, которые содержат до 5000 символов (хотя это будет редко). Я также использую Python 2.6 в настоящее время.

Изменить # 2 Я принял Tor Valamos как правильный, поскольку он не только дал правильное решение (хотя это было не лучшее решение), но и принял во внимание всех остальных, и сделал огромную работу, чтобы сравнить их все. Этот ответ - один из лучших, наиболее полезных ответов, которые я когда-либо встречал в StackOverflow. Престижность к вам.

Ответ 1

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

Я взял на себя смелость сравнить несколько решений:

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

Результаты в 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

Оба решения claudiu и andrew продолжали идти в 0, поэтому мне пришлось увеличить его до 10 000 пробегов.

Я запускал его в Python 3 (из-за юникода) с заменой символов от 39 до 1024 (38 - амперсанд, поэтому я не хотел его включать). Длина строки до 10.000, включая около 980 замен с переменными случайными вставками длиной 0-20. Значения unicode от 39 до 1024 заставляют символы длиной 1 и 2 байта, что может повлиять на некоторые решения.

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)

Результаты:

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

(** Обратите внимание, что в коде gnibbler используется другой dict, в котором ключи не включают "&". Код Andrew также использует этот альтернативный dict, но он не имеет большого значения, может быть, всего 0,01 x ускорение.)

Ответ 2

Попробуйте это, используя подстановку регулярных выражений и стандартное форматирование строк:

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

Вызов re.sub() заменяет все последовательности амперсандов, за которыми следует одна буква с шаблоном% (..) s, содержащим тот же шаблон.

В форматировании% используется функция форматирования строк, которая может принимать словарь для указания подстановки, а не более часто встречающиеся позиционные аргументы.

Альтернатива может сделать это непосредственно в re.sub, используя обратный вызов:

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

На этот раз я использую закрытие для ссылки на словарь изнутри функции обратного вызова. Такой подход может дать вам немного большую гибкость. Например, вы можете использовать что-то вроде dict.get(m.group(), '??'), чтобы избежать создания исключений, если у вас были строки с непризнанными кодовыми последовательностями.

(Кстати, как "dict", так и "str" являются встроенными функциями, и вы столкнетесь с проблемами, если будете использовать эти имена в своем собственном коде. На всякий случай, когда вы этого не знали. отлично для такого вопроса, конечно.)

Изменить: Я решил проверить тестовый код Tor и пришел к выводу, что он нигде рядом с представителем и на самом деле не работает. Сгенерированная строка не содержит в ней амперсандов (!). В приведенном ниже коде генерируется репрезентативный словарь и строка, аналогичные входным примерам OP.

Я также хотел проверить, что каждый выход алгоритма был таким же. Ниже приведена пересмотренная тестовая программа с кодами Tor, mine и Claudiu, потому что другие были разбиты на ввод образцов. (Я думаю, что они все хрупкие, если словарь не отображает в основном все возможные последовательности амперсандов, которые выполнял тестовый код Tor). Это правильно семена генератора случайных чисел, поэтому каждый прогон одинаковый. Наконец, я добавил незначительные вариации с использованием генератора, который позволяет избежать некоторых служебных вызовов функций, для незначительного улучшения производительности.

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

Я забыл включить результаты тестов раньше:

    Running 10000 times with string length 1000 and 96 ampersands
    ('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)

Кроме того, фрагменты входов и правильный вывод:

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

Сравнение с тем, что я увидел из вывода тестового кода Tor:

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

Ответ 3

Если вы действительно хотите вникнуть в тему, посмотрите на это: http://en.wikipedia.org/wiki/Aho-Corasick_algorithm

Очевидное решение путем итерации по словарю и замены каждого элемента в строке занимает время O(n*m), где n - размер словаря, m - длина строки.

В то время как алгоритм Ахо-Корасика находит все записи словаря в O(n+m+f), где f - количество найденных элементов.

Ответ 4

Если количество клавиш в списке велико, а число вхождений в строке низкое (и в основном ноль), вы можете перебирать вхождения амперсандов в строке и использовать словарь с ключом по первому символу подстрок. Я часто не кодирую код в python, поэтому стиль может быть немного выключен, но вот мой подход к нему:

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

Конечно, возникает вопрос, что происходит, когда есть амперсанд, который исходит из самой строки, вам нужно будет каким-то образом избежать этого, прежде чем подавать этот процесс, а затем unescape после этого процесса.

Конечно, как это обычно бывает с проблемами производительности, выбор времени на различные подходы к типичному (а также наихудшему) набору данных и их сравнение - это хорошо.

EDIT: поместите его в отдельную функцию для работы с произвольным словарем:

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

EDIT2: избавиться от ненужной конкатенации, по-прежнему будет немного быстрее, чем предыдущий на многих итерациях.

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

Ответ 5

Вот подпрограмма C-расширений для 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, которые я тестировал

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]

Результаты

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.

Предположим, что он может работать при O (n) и Только 160 мс (avg) для 11 МБ в My Mobile Celeron 1,6 ГГц ПК

Он также пропустит неизвестные символы как есть, например &Unknown вернется как есть

Сообщите мне, если у вас возникли проблемы с компиляцией, ошибками и т.д.

Ответ 6

Это похоже на то, что вам нужно - несколько строк сразу заменяются с помощью RegExps. Вот соответствующий код:

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)

Ответ 7

Общее решение для определения правил замены заключается в использовании замены regex с использованием функции для предоставления карты (см. 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 )

Это особенно хорошо для нетривиальных подстановок (например, что-либо, требующее математических действий для создания замены).

Ответ 8

Вот версия, использующая 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)

Если есть амперсанды с неверными кодами, вы можете использовать их для сохранения

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

Питер Хансен отметил, что это не удается, когда есть двойной амперсанд. В этом случае используйте эту версию

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)

Ответ 9

Не уверен в скорости этого решения, но вы можете просто пропустить свой словарь и многократно вызывать встроенный

str.replace(old, new)

Это может проявляться прилично, если исходная строка не слишком длинная, но она, очевидно, пострадает, поскольку строка будет длиннее.

Ответ 10

Проблема с заменой этой массы в Python - неизменность строк: каждый раз, когда вы заменяете один элемент в строке, вся новая строка будет перераспределяться снова и снова из кучи.

Итак, если вам нужно самое быстрое решение, вам нужно либо использовать изменяемый контейнер (например, список), либо написать это оборудование в простой C (или лучше в Pyrex или Cython). В любом случае я бы предложил написать простой синтаксический анализатор на основе простой конечной машины и поочередно кормить символы вашей строки.

Предлагаемые решения на основе регулярных выражений работают аналогично, потому что regexp работает с использованием fsm за сценой.

Ответ 11

С тех пор, как кто-то упомянул об использовании простого синтаксического анализатора, мне показалось, что я готовлю его, используя pyparsing. Используя метод pyparsing transformString, pyparsing внутренне сканирует исходную строку и строит список совпадающего текста и промежуточного текста. Когда все будет сделано, transformString будет "." Присоединиться к этому списку, поэтому нет проблемы с производительностью при наращивании строк с помощью приращений. (Действие синтаксического анализа, определенное для ANSIreplacer, выполняет преобразование из совпадающих символов & _ в требуемую escape-последовательность и заменяет согласованный текст на результат действия синтаксического анализа. Поскольку только соответствующие последовательности удовлетворяют выражению парсера, нет необходимости для действия синтаксического анализа для обработки последовательностей undefined & _.)

FollowedBy ('&') не является строго необходимым, но он ускоряет процесс разбора, проверяя, что синтаксический анализатор фактически позиционируется на амперсанде, прежде чем выполнять более дорогостоящую проверку всех параметров разметки.

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)

Печать

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

Это, конечно же, не приведет к победе в каких-либо конкурсах производительности, но если ваша разметка начнет усложняться, то наличие основы анализатора упростит расширение.

Ответ 12

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

Ответ 13

попробуйте это

tr.replace( "& у", ДИКТ [ "& у" ])

tr.replace( "& C", ДИКТ [ "& с" ])

tr.replace( "& б", ДИКТ [ "& б" ])

tr.replace( "& Y" ДИКТ [ "& Y" ])

tr.replace( "& U", ДИКТ [ "& и" ])