import re
text = '\x1b[0m\x1b[01;32mattr\x1b[0m\n\x1b[01;36mawk\x1b[0m\n\x1b[01;32mbasename\x1b[0m\n\x1b[01;32mbash\n\x1b[0many text'
# dictionary mapping text attributes to tag names
fmt = {'01':'bold', '32m':'green', '36m': 'cyan'}
# regex that gets all text attributes, the text and any potential newline
groups = re.findall('(\n?)\\x1b\[((?:(?:0m|32m|01|36m);?)+)([a-zA-Z ]+)', text)
# iterate through the groups and build your new string
xml = []
for group in groups:
g_text = group[2] # the text itself
for tag in group[1].split(';'): # the text attributes
if tag in fmt:
tag = fmt[tag]
else:
continue
g_text = '<%s>%s</%s>' %(tag,g_text,tag)
g_text = group[0] + g_text # add a newline if necessary
xml.append(g_text)
xml_text = ''.join(xml)
print(xml_text)
<green><bold>attr</bold></green>
<cyan><bold>awk</bold></cyan>
<green><bold>basename</bold></green>
<green><bold>bash</bold></green>
any text
Для демонстрации на регулярных выражений смотрите по этой ссылке: Debuggex Demo
В настоящее время регулярное выражение предполагает, что вы только имеют альфа-символы или пробелы в фактическом тексте, но не стесняйтесь изменять эту группу ([a-zA-Z ]+)
в конце регулярного выражения, чтобы включить другой символ которые вы можете иметь в своем тексте.
Кроме того, я предполагаю, что у вас больше атрибутов текста, чем полужирный, зеленый и голубой. Вам нужно будет обновить словарь fmt
другими вашими атрибутами и их сопоставлениями.
EDIT
@Caaarlos запросило в комментариях (ниже), чтобы сохранить код AnSi как на выходе, если он не появляется в fmt
словаре:
import re
text = '\x1b[0m\x1b[01;32;35mattr\x1b[0;7m\n\x1b[01;36mawk\x1b[0m\n\x1b[01;32;47mbasename\x1b[0m\n\x1b[01;32mbash\n\x1b[0many text'
fmt = {'01':'bold', '32':'green', '36': 'cyan'}
xml = []
active_tags = []
for group in re.split('\x1b\[', text):
if group.strip():
codes, text = re.split('((?:\d+;?)+)m', group)[1:]
not_found = []
for tag in codes.split(';'):
if tag in fmt:
tag = fmt[tag]
text = '<%s>%s' %(tag,text)
active_tags.append(tag)
elif tag == '0':
for a_tag in active_tags[::-1]:
text = '</%s>%s' %(a_tag,text)
active_tags = []
else:
not_found.append(tag)
if not_found:
text = '\x1b[%sm%s' %(';'.join(not_found), text)
xml.append(text)
xml_text = ''.join(xml)
print(repr(xml_text))
'\x1b[35m<green><bold>attr\x1b[7m</bold></green>\n<cyan><bold>awk</bold></cyan>\n\x1b[47m<green><bold>basename</bold></green>\n<green><bold>bash\n</bold></green>any text'
Обратите внимание, что отредактированный код выше также обрабатывает случаи, когда тег не закрывается непосредственно после текста.
Спасибо, Бунджи, он работает правильно. Но, когда у меня нет шаблона в fmt, я хочу, чтобы моя текстовая переменная продолжала иметь escape-последовательность ascii (цели отладки). Например, если есть не '36m' шаблон, я хочу, чтобы мой окончательный вывод будет: атр \ X1B [01; 36mawk базовое Баш любой текст –
Caaarlos
@Caaarlos, ответ был обновлен с помощью редактирования, который обрабатывает коды, не найденные в fmt. Кроме того, он обрабатывает случаи, когда тег не должен закрываться непосредственно после текста. – bunji