5

我在使用 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​​ 的控制台中排列彩色表格数据的最佳实践方法?

4

5 回答 5

5

这不是一个错误:ruby 不可能知道(至少在 printf 中,对于像 curses 这样的东西来说,这将是一个不同的故事)它的标准输出将进入一个理解 VT100 转义序列的终端。

如果你不调整背景颜色,这样的事情可能是一个更好的主意:

GREEN = "\033[32m"
NORMAL = "\033[0m"
printf "%s%20s%s\n", GREEN, "Green", NORMAL
于 2010-09-16T03:13:13.507 回答
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)。

于 2010-09-16T03:35:17.957 回答
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终端列。这将涉及解析转义和处理多字节字符并使用工具(如 POSIXwcwidth函数)来查找每个终端列的数量。请注意使用*in 代替printf格式字符串中的恒定字段宽度。这允许您将int参数传递printf给运行时可变字段宽度。

于 2010-09-16T03:24:22.273 回答
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,它们提供了对许多不同类型的终端工作的颜色更改功能。它们还提供更多功能,例如基于文本的窗口、功能键,有时甚至是鼠标交互。

于 2010-09-16T14:51:15.770 回答
0

这是我最近想出的一个解决方案。这允许您color("my string", :red)printf语句中使用。我喜欢对标题和数据使用相同的格式字符串——DRY。这使这成为可能。另外,我使用彩虹宝石生成颜色代码;它并不完美,但可以完成工作。哈希包含每种颜色的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)
]
于 2012-03-16T17:06:13.087 回答