获取正确的字符串长度的Python为符合ANSI颜色代码串
-
25-09-2019 - |
题
我有一些Python代码,将自动打印的一组数据在一个不错的列格式,包括把在相应的ASCII转义序列来颜色的各条数据的以提高可读性。
我最终结束了与每个线路被表示为列表,与每个项目是一个列,它是空间填充,使得在每行上的相同的列总是相同的长度。不幸的是,当我真正去打印此,不是所有的列排队。我怀疑这是做的ASCII转义序列 - 因为len
功能似乎并没有认识到这些:
>>> a = '\x1b[1m0.0\x1b[0m'
>>> len(a)
11
>>> print a
0.0
和因此而每一列是根据len
相同的长度,它们在屏幕上打印时实际上不是相同的长度。
有什么办法(除了做一些两轮牛车正则表达式,我宁愿不做)采取转义字符串,并找出印刷长度是多少,所以我能空间垫是否正确?也许某种程度上只是“打印”回字符串,并考察了长?
解决方案
在pyparsing维基包括用于在ANSI转义序列匹配此有用表达:
ESC = Literal('\x1b')
integer = Word(nums)
escapeSeq = Combine(ESC + '[' + Optional(delimitedList(integer,';')) +
oneOf(list(alphas)))
下面是如何使成转义序列 - 汽提这样:
from pyparsing import *
ESC = Literal('\x1b')
integer = Word(nums)
escapeSeq = Combine(ESC + '[' + Optional(delimitedList(integer,';')) +
oneOf(list(alphas)))
nonAnsiString = lambda s : Suppress(escapeSeq).transformString(s)
unColorString = nonAnsiString('\x1b[1m0.0\x1b[0m')
print unColorString, len(unColorString)
打印:
0.0 3
其他提示
我不明白两件事情。
(1)它是你的代码,您的控制之下。要转义序列添加到您的数据,然后带他们出去一次,这样就可以计算出你的数据的长度?这似乎简单得多来计算填充的 强>加入转义序列之前。我缺少什么?
让我们假定没有转义序列的改变光标的位置。如果他们这样做,目前公认的答案无论如何都不会工作。
让我们假设你有各列的字符串数据中一列表中指定string_data
和预先确定的列的宽度(添加转义序列之前)是在名为width
列表。尝试是这样的:
temp = []
for colx, text in enumerate(string_data):
npad = width[colx] - len(text) # calculate padding size
assert npad >= 0
enhanced = fancy_text(text, colx, etc, whatever) # add escape sequences
temp.append(enhanced + " " * npad)
sys.stdout.write("".join(temp))
<强>更新-1 强>
OP的评论后:
欲剥离出来,并后的计算长度的原因 字符串包含颜色代码是因为所有的数据建立起来的 编程。我有一大堆的彩色化的方法和我建立 备份数据是这样的:
str = "%s/%s/%s" % (GREEN(data1), BLUE(data2), RED(data3))
这将是非常困难的颜色 事实后的文本。
如果该数据是建立每个具有其自己的格式件起来,仍可以计算所显示的长度和垫适当。下面是其确实,对于一个单元格的内容的函数:
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(40, 48)
BOLD = 1
def render_and_pad(reqd_width, components, sep="/"):
temp = []
actual_width = 0
for fmt_code, text in components:
actual_width += len(text)
strg = "\x1b[%dm%s\x1b[m" % (fmt_code, text)
temp.append(strg)
if temp:
actual_width += len(temp) - 1
npad = reqd_width - actual_width
assert npad >= 0
return sep.join(temp) + " " * npad
print repr(
render_and_pad(20, zip([BOLD, GREEN, YELLOW], ["foo", "bar", "zot"]))
)
如果你认为呼叫标点符号不堪重负,你可以这样做:
BOLD = lambda s: (1, s)
BLACK = lambda s: (40, s)
# etc
def render_and_pad(reqd_width, sep, *components):
# etc
x = render_and_pad(20, '/', BOLD(data1), GREEN(data2), YELLOW(data3))
(2)我不明白你为什么不想使用所提供的与 - Python的正则表达式包?否“两轮牛车”(为“两轮牛车”我所知道的任何可能的意思)参与:
>>> import re
>>> test = "1\x1b[a2\x1b[42b3\x1b[98;99c4\x1b[77;66;55d5"
>>> expected = "12345"
>>> # regex = re.compile(r"\x1b\[[;\d]*[A-Za-z]")
... regex = re.compile(r"""
... \x1b # literal ESC
... \[ # literal [
... [;\d]* # zero or more digits or semicolons
... [A-Za-z] # a letter
... """, re.VERBOSE)
>>> print regex.findall(test)
['\x1b[a', '\x1b[42b', '\x1b[98;99c', '\x1b[77;66;55d']
>>> actual = regex.sub("", test)
>>> print repr(actual)
'12345'
>>> assert actual == expected
>>>
<强>更新-2 强>
OP的评论后:
我还是喜欢保罗的答案,因为它更简洁
更简洁比什么?是不是正则表达式如下解决方案简洁够吗?
# === setup ===
import re
strip_ANSI_escape_sequences_sub = re.compile(r"""
\x1b # literal ESC
\[ # literal [
[;\d]* # zero or more digits or semicolons
[A-Za-z] # a letter
""", re.VERBOSE).sub
def strip_ANSI_escape_sequences(s):
return strip_ANSI_escape_sequences_sub("", s)
# === usage ===
raw_data = strip_ANSI_escape_sequences(formatted_data)
[以上代码校正后@Nick珀金斯指出,它不工作]
寻找在 ANSI_escape_code 时,在您的示例的序列是 选择图形再现强>(可能粗体强>)。
尝试控制柱定位用的光标位置强>(CSI n ; m H
)序列。
以这种方式,前面的文字的宽度不影响当前列位置,并且也没有必要对有关字符串宽度担心。
一个更好的选择,如果您定位的Unix,使用诅咒模块窗口-objects 。 例如,串可被定位在屏幕上:
window.addnstr([y, x], str, n[, attr])
在油漆在字符串str的最多n个字符(Y,X)与属性ATTR,在显示器上先前覆盖任何东西。
如果你只是添加颜色到一些细胞,可以添加9到预期小区宽度(5个隐藏字符到打开色彩,4将其关闭),例如
import colorama # handle ANSI codes on Windows
colorama.init()
RED = '\033[91m' # 5 chars
GREEN = '\033[92m' # 5 chars
RESET = '\033[0m' # 4 chars
def red(s):
"color a string red"
return RED + s + RESET
def green(s):
"color a string green"
return GREEN + s + RESET
def redgreen(v, fmt, sign=1):
"color a value v red or green, depending on sign of value"
s = fmt.format(v)
return red(s) if (v*sign)<0 else green(s)
header_format = "{:9} {:5} {:>8} {:10} {:10} {:9} {:>8}"
row_format = "{:9} {:5} {:8.2f} {:>19} {:>19} {:>18} {:>17}"
print(header_format.format("Type","Trial","Epsilon","Avg Reward","Violations", "Accidents","Status"))
# some dummy data
testing = True
ntrials = 3
nsteps = 1
reward = 0.95
actions = [0,1,0,0,1]
d = {'success': True}
epsilon = 0.1
for trial in range(ntrials):
trial_type = "Testing " if testing else "Training"
avg_reward = redgreen(float(reward)/nsteps, "{:.2f}")
violations = redgreen(actions[1] + actions[2], "{:d}", -1)
accidents = redgreen(actions[3] + actions[4], "{:d}", -1)
status = green("On time") if d['success'] else red("Late")
print(row_format.format(trial_type, trial, epsilon, avg_reward, violations, accidents, status))
给予