如何阻止 ANSI 颜色代码扰乱 printf 对齐?
-
03-10-2019 - |
题
我在使用 ruby printf 时发现了这一点,但它也适用于 C 的 printf。
如果在输出字符串中包含 ANSI 颜色转义代码,则会弄乱对齐方式。
红宝石:
ruby-1.9.2-head > printf "%20s\n%20s\n", "\033[32mGreen\033[0m", "Green"
Green # 6 spaces to the left of this one
Green # correctly padded to 20 chars
=> nil
C 程序中的同一行会产生相同的输出。
无论如何,有没有办法让 printf (或其他东西)对齐输出而不为非打印字符添加空格?
这是一个错误,还是有充分的理由?
更新: 由于当存在 ANSI 代码和宽字符时,不能依赖 printf 来对齐数据,因此是否有最佳实践方法可以在 ruby 控制台中排列彩色表格数据?
解决方案
我不同意您对绿色绿色后9个空间的特征。我使用Perl而不是Ruby,但是如果我使用您的语句修改,在字符串后打印管道符号,我会得到:
perl -e 'printf "%20s|\n%20s|\n", "\033[32mGreen\033[0m", "Green";'
Green|
Green|
这向我表明了 printf()
语句在字符串中计算了14个字符,因此它预先固定了6个空间,以制作20个字符的直接对准。但是,终端吞下了其中9个字符,将其解释为颜色变化。因此,输出显示的9个字符比您想要的短。然而 printf()
第一个“绿色”之后,没有打印9个空白。
关于对齐输出的最佳实践(与颜色),我认为您需要将每个尺寸和对准的字段包围着简单的“%s”字段,这些字段涉及颜色:
printf "%s%20.20s%s|%s%-10d%s|%s%12.12s%s|\n",
co_green, column_1_data, co_plain,
co_blue, column_2_data, co_plain,
co_red, column_3_data, co_plain;
显然,在哪里 co_XXXX
变量(常数?)包含要切换到命名颜色的逃生序列(和 co_plain
可能会更好 co_black
)。如果事实证明您不需要在某些字段上进行颜色,则可以使用空字符串代替 co_XXXX
变量(或调用 co_empty
).
其他提示
这不是一个错误:Ruby不应该知道(至少在printf中,对于像诅咒之类的东西是另一个故事),它的Stdout将进入一个了解VT100逃生序列的终端。
如果您没有调整背景颜色,那么这样的事情可能是一个更好的主意:
GREEN = "\033[32m"
NORMAL = "\033[0m"
printf "%s%20s%s\n", GREEN, "Green", NORMAL
printf
字段宽度指示符对于对齐表格数据,接口元素等都没有用,除了您已经发现的控制字符的问题外,还有非课程和双宽字符,如果您不don',您的程序还必须处理这些字符。 T希望将内容限制为旧字符编码(许多用户认为已弃用)。
如果您坚持使用 printf
这样,您可能需要做类似的事情:
printf("%*s\n%*s\n", bytestopad("\033[32mGreen\033[0m", 20), "\033[32mGreen\033[0m", bytestopad("Green", 20), "Green");
在哪里 bytestopad(s,n)
是您编写的函数,该函数计算需要多少个字节(字符串加填充空间)才能导致字符串 s
占用 n
终端列。这将涉及解析逃脱和处理多材料字符并使用设施(例如Posix wcwidth
函数)查找每个终端列。注意使用 *
代替恒定的场宽度 printf
格式字符串。这使您可以通过 int
争论 printf
用于运行时变量的字段宽度。
我会从实际文本中分离出任何转义序列,以避免整个问题。
# in Ruby
printf "%s%20s\n%s%20s\n", "\033[32m", "Green", "\033[0m", "Green"
或者
/* In C */
printf("%s%20s\n%s%20s\n", "\033[32m", "Green", "\033[0m", "Green");
由于 ANSI 转义序列不是 Ruby 或 C 的一部分,因此它们并不认为需要特殊对待这些字符,而且理应如此。
如果您要做很多终端颜色的工作,那么您应该研究curses 和ncurses,它们提供了适用于许多不同类型终端的颜色更改功能。它们还提供更多的功能,例如基于文本的窗口、功能键,有时甚至是鼠标交互。
这是我最近提出的解决方案。这使您可以使用 color("my string", :red)
在一个 printf
陈述。我喜欢使用相同的格式字符串作为标头和数据 - 干燥。这使得这是可能的。另外,我使用 彩虹 宝石生成颜色代码;这不是完美的,但要完成工作。这 CPAD
哈希分别包含每个颜色的两个值,分别对应于左右填充。自然,应扩展该解决方案,以促进其他颜色和修饰符,例如大胆和下划线。
CPAD = {
:default => [0, 2],
:green => [0, 3],
:yellow => [0, 2],
:red => [0, 1],
}
def color(text, color)
"%*s%s%*s" % [CPAD[color][0], '', text.color(color), CPAD[color][1], '']
end
例子:
puts "%-10s %-10s %-10s %-10s" % [
color('apple', :red),
color('pear', :green),
color('banana', :yellow)
color('kiwi', :default)
]