我如何计算各种字符的终端列宽?

How might I calculate the terminal column width of various characters?

我正在计算各种打印和非打印 ascii/unicode 字符将在终端视图中占用的终端列数。

例如,水平制表符(\t)占8列,颜色代码(即\x1b32m)占0列,固定大小的宽字符串(即한)可能占2列列。当然在初级ASCII集中有很多只占1列(即a-Z/0-9,标点符号等)。

我遇到了 node.js 模块,wcwidth,它似乎有助于计算宽字符字符串,但对其他字符(如颜色)却没有达到我的预期代码和标签。

例如:

var wcwidth = require('wcwidth');

console.log("TAB WIDTH", wcwidth('\t'));
console.log("한 WIDTH", wcwidth('한'));
console.log("Color Code WIDTH", wcwidth('\x1b32m'));
console.log("X WIDTH", wcwidth('X'));

输出:

TAB WIDTH 0
한 WIDTH 2
Color Code WIDTH 3
X WIDTH 1

我似乎无法在任何地方找到有关此的任何信息,但我认为这将是人们在古代必须解决的常见问题。

如果有可能使用 bash 脚本或任何库、应用程序或工具,我也完全愿意。

非常感谢任何帮助! :) 谢谢

对于任何需要知道光标在屏幕上的位置的程序来说,这确实是一个问题,从 ls 中的表格输出到可编辑的命令行再到全屏应用程序。正如您所注意到的,它不是由 wcwidth 或 wcswidth 解决的,wcwidth 或 wcswidth 仅为可打印字符(的字符串)定义。 (即使是 not well defined 对于许多字符。)此外,控制序列不仅可以更改颜色,还可以更改光标定位,甚至在支持的情况下还可以更改字体大小效果。

相反,有时会使用 ncurses [npm search] 等终端控制库。这些似乎也不会告诉您字符串宽度,但因为它们单独跟踪文本属性(例如颜色),并自行生成控制序列来定位和设置文本样式,所以它们提供了一些帮助,可以将内容放在屏幕上的给定位置。

不幸的是,我认为除此之外没有太多可用的,应用程序要么忽略复杂性,要​​么以临时方式处理它们。


澄清一个常见的误解:水平制表符(HT,\t)本身没有宽度;它是 'format effector',如 Carriage Return 或 Form Feed,根据特定规则重新定位光标。

HT (Horizontal Tabulation): A format effector which controls the movement of the printing position to the next in a series of predetermined positions along the printing line. (Applicable also to display devices and the skip function on punched cards.)

— USA Standard Code for Information Interchange [ASCII], 1968, as reprinted in RFC 20

最常见的实现是每八列固定 制表位

                                1       2
                1.......9.......7.......5.....

1\tXYZ          1       XYZ
12\tXYZ         12      XYZ
1234567\tXYZ    1234567 XYZ
12345678\tXYZ   12345678        XYZ
123456789\tXYZ  123456789       XYZ

虽然某些系统支持控制序列或其他方式来设置任意距离的制表位位置,例如某些文字处理器中的标尺栏。

一个tab不占8列。它输出一个 space 然后足够的 spaces 以确保下一个字符将在索引为 0 mod 8 (或 1 mod 8如果你从 1 开始计算。)换句话说,除非你知道你在这条线上的位置,否则你无法知道制表符有多宽。

颜色代码 (\x1b[32m) 可能占用零 space,但也可能不占用;这取决于控制台终端仿真器的性质。大多数终端仿真器会识别 CSI[Pm 代码,但还有其他代码更加特殊。例如,

printf $'\x1b]2;A window\x1b\'

将在 xterm 中设置 window 标题,因此不会产生任何输出。但是在Linux控制台中,会显示文本;A window,占用9个字符。

总之,这不是一个简单的问题,没有绝对的答案,你只能结合很多上下文来回答。