这个问题仅限于没有空格的字符串,这些字符串是故意编写的,以供人类阅读。
我不关心NUL或其他字符,这些字符在一篇供人类阅读的文本中找不到。
此外,我也不关心“病态”案例,比如
# !/usr/bin/env perl
use strict; use warnings;
use feature 'say';
say 'dog', "\r", 'rat';
say 'a', "\b", 'z';
例如,当文本不全是ASCII时,这个问题对于生成居中的文本行很有用。
在下面的Perl脚本中,我们首先查看占据1列的字符串,然后是2列、3列等等。
正如我们从运行这段代码中看到的那样,无论是字节数,还是通过在B
处拆分字符串而创建的数组的长度,都不能可靠地告诉我们打印时一个字符串或一个字符将占据多少列。有没有办法弄到这个号码?
# !/usr/bin/env perl
use strict; use warnings;
use feature 'say';
while(<DATA>)
{
say '------------------------------------------';
print;
$_=~s/\s//g;
my@array=split /\B/,$_;
say length $_,' bytes, ',scalar@array,' components';
}
__DATA__
a
é
ø
ü
α
ά
∩
⊃
≈
≠
好
üb
üü
dog
Voß
café
Schwiizertüütsch
1条答案
按热度按时间mnowg1ta1#
终端用于打印文本的列数直接由打印的“字符”数确定,其中每个字符可能需要0、1或2列来打印。
这些是逻辑Unicode字符,扩展的字素簇。它们可以是字符序列,通常是基本字符及其combining diacritical marks(重音),或者可以具有单个码点,但表示来自特定书写系统/语言的一个字符。
然后需要的是以一种尊重Unicode的方式将输入分解成字符,并找出每个字符需要多少列。(好吧,或者使用一个可以做到这一点的库。)
查看宽度的一种方法是使用正则表达式测试
East_Asian_Width
属性,以及p{East_Asian_Width=Wide}
或p{EA=W}
。请参见perluniprops(+perluniintro,perlunicode)。或者,转到核心模块Unicode::UCD,它连接Unicode字符数据库并具有all属性。
这些值可以是:中性(非东亚)、宽、模糊、窄、全宽和半宽--根据上下文的不同,这些值总是可以解析为两个、窄或宽。请参阅Unicode标准附件中关于东亚宽度的所有详细信息,UAX#11,或参见上面链接的
perluniprops
中的列表,该列表还显示了它们被发现的频率;列出的前三个列表比其他三个列表更频繁地出现。这个列表中最特别的是
Ambiguous
,它可以是宽的也可以是窄的,这取决于它的使用环境(无论它是不是东亚人),它包括了所有的细节;请参见链接。鉴于这个问题似乎有必要,我现在就不提了,把它当作狭隘的问题来处理。则需要2列的唯一属性值将是Wide
。一个例子
如果我们要使用字符数据库,那么我们还需要字符的代码点,例如
这将生成上面列出的名称。
我们还需要为程序启用Unicode支持(这是问题中的示例程序失败的地方):utf8 pragma在那里是因为源文件本身包含Unicode字符,而open pragma负责标准流。
记住,所有这些都忽略了
Ambiguous
,即。我们认为它们都是狭隘的,这通常是不正确的。改进这一点的最简单方法是使用库Unicode::GCString,有了它,事情就变得几乎微不足道了
虽然这个库显然是经过深思熟虑并享有盛誉的,但这确实伴随着一个警告。在几年前的最近一次有意义的更新中,该库使用Unicode标准8.0.0(如它使用的Unicode::LineBreak中所述),这严重过时了,这可能会导致错误(参见示例here)。
与手动方法甚至没有解决宽度不明确的上下文的事实相比,这是一个nit。但这个问题上下文中的一个重要问题是,这是一个外部模块,它需要一个C库(sombok),并且似乎不再更新。
感谢
tchrist
提出了一些这方面的问题,从而引发了更详细的讨论。如果这里的预期用途不涉及宽字符,那么它非常简单
(然后提供的输入中的中文字符将不正确,但在这个用例中可能实际上不需要。)
X
是匹配逻辑字符的一种方法,请参见同一页上的b{gcb}
。这里不需要捕获(X)
,因为我们希望所有这些都匹配,所以my @egc = /X/g;
就可以了。但这不会有什么坏处,如果图案中有更多的东西,人们可能会需要它,所以我放了()
。请原谅我使用
0+@ary
表示数组大小,因为我正在尝试在显示宽度中容纳一行代码以便于阅读;为此,请务必使用scalar @ary
。通过添加问题代码上方的语用标记,以及来自length的语句,我觉得很有启发意义
返回expr值的长度,单位为个字符。
..。
与所有Perl字符操作一样,
length
通常处理逻辑字符,而不是物理字节。(原文强调)
感谢Thomas Dickey在最初的帖子中呼吁省略字符宽度的讨论。