gpt4 book ai didi

c - 汉字UTF-8宽度显示问题

转载 作者:太空狗 更新时间:2023-10-29 14:59:33 25 4
gpt4 key购买 nike

当我使用 Perl 或 C 来 printf 一些数据时,我尝试了它们的格式来控制每列的​​宽度,比如

printf("%-30s", str);

但是当 str 包含汉字时,列不会按预期对齐。见附件图片。

我的ubuntu的charset编码是zh_CN.utf8,据我所知,utf-8编码有1~4个字节的长度。汉字有3个字节。在我的测试中,我发现printf的格式控制把一个汉字算作3个,但它实际上显示为2个ascii宽度。

所以真正的显示宽度并不是预期的常量而是一个与汉字个数有关的变量,即

Sw(x) = 1 * (w - 3x) + 2 * x = w - x

w为期望的宽度限制,x为汉字数,Sw(x)为实际显示宽度。

所以str包含的汉字越多,显示的越短。

我怎样才能得到我想要的?统计printf前的汉字?

据我所知,所有中文甚至我猜的所有宽字符都显示为2宽度,那为什么printf会把它算作3? UTF-8的编码与显示长度无关。

最佳答案

是的,这是我所知道的所有 printf 版本的问题。我在 this answer 中简要讨论此事还有this one .

对于 C,我不知道有哪个库可以为您做这件事,但如果有人有的话,那就是 ICU。

对于 Perl,您必须使用 Unicode::GCString模块形式 CPAN 来计算一个 Unicode 字符串将占用的打印列数。这考虑到了 Unicode Standard Annex #11: East Asian Width .

例如,一些代码点占用 1 列,其他代码点占用 2 列。甚至有些根本不占用任何列,例如组合字符和不可见的控制字符。该类有一个 columns 方法,该方法返回字符串占用的列数。

我有一个使用它来垂直对齐 Unicode 文本的示例 here .它将对一堆 Unicode 字符串进行排序,包括一些带有组合字符和“宽”亚洲表意文字(CJK 字符)的字符串,并允许您垂直对齐。

sample terminal output

umenu 小演示程序的代码包含在下面,它打印出很好对齐的输出。

您可能还对雄心勃勃的 Unicode::LineBreak 感兴趣模块,其中前面提到的 Unicode::GCString 类只是一个较小的组件。这个模块更酷,并考虑到Unicode Standard Annex #14: Unicode Line Breaking Algorithm .

这是在 Perl v5.14 上测试的 umenu 小演示的代码:

 #!/usr/bin/env perl
# umenu - demo sorting and printing of Unicode food
#
# (obligatory and increasingly long preamble)
#
use utf8;
use v5.14; # for locale sorting
use strict;
use warnings;
use warnings qw(FATAL utf8); # fatalize encoding faults
use open qw(:std :utf8); # undeclared streams in UTF-8
use charnames qw(:full :short); # unneeded in v5.16

# std modules
use Unicode::Normalize; # std perl distro as of v5.8
use List::Util qw(max); # std perl distro as of v5.10
use Unicode::Collate::Locale; # std perl distro as of v5.14

# cpan modules
use Unicode::GCString; # from CPAN

# forward defs
sub pad($$$);
sub colwidth(_);
sub entitle(_);

my %price = (
"γύρος" => 6.50, # gyros, Greek
"pears" => 2.00, # like um, pears
"linguiça" => 7.00, # spicy sausage, Portuguese
"xoriço" => 3.00, # chorizo sausage, Catalan
"hamburger" => 6.00, # burgermeister meisterburger
"éclair" => 1.60, # dessert, French
"smørbrød" => 5.75, # sandwiches, Norwegian
"spätzle" => 5.50, # Bayerisch noodles, little sparrows
"包子" => 7.50, # bao1 zi5, steamed pork buns, Mandarin
"jamón serrano" => 4.45, # country ham, Spanish
"pêches" => 2.25, # peaches, French
"シュークリーム" => 1.85, # cream-filled pastry like éclair, Japanese
"막걸리" => 4.00, # makgeolli, Korean rice wine
"寿司" => 9.99, # sushi, Japanese
"おもち" => 2.65, # omochi, rice cakes, Japanese
"crème brûlée" => 2.00, # tasty broiled cream, French
"fideuà" => 4.20, # more noodles, Valencian (Catalan=fideuada)
"pâté" => 4.15, # gooseliver paste, French
"お好み焼き" => 8.00, # okonomiyaki, Japanese
);

my $width = 5 + max map { colwidth } keys %price;

# So the Asian stuff comes out in an order that someone
# who reads those scripts won't freak out over; the
# CJK stuff will be in JIS X 0208 order that way.
my $coll = new Unicode::Collate::Locale locale => "ja";

for my $item ($coll->sort(keys %price)) {
print pad(entitle($item), $width, ".");
printf " €%.2f\n", $price{$item};
}

sub pad($$$) {
my($str, $width, $padchar) = @_;
return $str . ($padchar x ($width - colwidth($str)));
}

sub colwidth(_) {
my($str) = @_;
return Unicode::GCString->new($str)->columns;
}

sub entitle(_) {
my($str) = @_;
$str =~ s{ (?=\pL)(\S) (\S*) }
{ ucfirst($1) . lc($2) }xge;
return $str;
}

如您所见,让它在那个特定程序中工作的关键是这行代码,它只调用上面定义的其他函数,并使用我正在讨论的模块:

print pad(entitle($item), $width, ".");

这将使用点作为填充字符将项目填充到给定的宽度。

是的,它没有 printf 方便,但至少它是可能的。

关于c - 汉字UTF-8宽度显示问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10751867/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com