我在使用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中在控制台中排列彩色表格数据?< / p>
答案 0 :(得分:5)
这不是一个错误:ruby不应该知道(至少在printf中,对于像curses这样的东西会是一个不同的故事),它的stdout会去一个了解VT100转义序列的终端。
如果你没有调整背景颜色,这样的事情可能是个更好的主意:
GREEN = "\033[32m"
NORMAL = "\033[0m"
printf "%s%20s%s\n", GREEN, "Green", NORMAL
答案 1 :(得分:4)
我不同意你对“绿色绿色之后的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
)。
答案 2 :(得分:3)
printf
字段宽度说明符对于对齐表格数据,界面元素等没有用。除了您已经发现的控制字符的问题之外,您的程序还会有非间距和双宽度字符。必须处理如果你不想限制遗留字符编码(很多用户认为已弃用)。
如果您坚持以这种方式使用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
以获取运行时变量字段宽度。
答案 3 :(得分:3)
我会从实际文本中删除任何转义序列以避免整个问题。
# 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,它们提供了可以用于许多不同类型终端的颜色变化的功能。它们还提供了更多功能,如基于文本的窗口,功能键,有时甚至是鼠标交互。
答案 4 :(得分:0)
这是我最近提出的解决方案。这允许您在color("my string", :red)
语句中使用printf
。我喜欢为标题和数据使用相同的格式字符串 - DRY。这使得这成为可能。另外,我使用rainbow gem生成颜色代码;它并不完美但完成工作。 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)
]