Да, это проблема всех известных мне версий printf
. Я кратко обсуждаю этот вопрос в этом ответе, а также в этот.
Что касается C, я не знаю библиотеки, которая сделает это за вас, но если она у кого-то есть, то это будет ICU.
Для Perl вы должны использовать модуль Unicode :: GCString из CPAN, чтобы рассчитать количество столбцов печати, которые займет строка Unicode. При этом учитывается стандартное приложение № 11 Unicode: ширина Восточной Азии.
Например, некоторые кодовые точки занимают 1 столбец, а другие - 2 столбца. Есть даже такие, которые вообще не занимают столбцов, например, объединение символов и невидимых управляющих символов. В классе есть метод columns
, который возвращает количество столбцов, занимаемых строкой.
У меня есть пример использования этого для выравнивания текста Unicode по вертикали здесь. Он отсортирует набор строк Unicode, в том числе некоторые с комбинированными символами и «широкими» азиатскими идеограммами (символы CJK), и позволит вам выровнять их по вертикали.
![образец вывода терминала](https://i.stack.imgur.com/ifmnX.png)
Ниже приведен код небольшой umenu
демонстрационной программы, которая распечатывает этот красиво выровненный вывод.
Возможно, вас заинтересует гораздо более амбициозный модуль Unicode :: LineBreak из который вышеупомянутый класс Unicode::GCString
является лишь меньшим по размеру компонентом. Этот модуль намного круче и учитывает стандартное приложение № 14 Unicode: алгоритм разрыва строки Unicode.
Вот код небольшой umenu
демонстрации, протестированной на Perl v5.14:
#!/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
, но, по крайней мере, это возможно.
person
tchrist
schedule
26.05.2012
printf
для Perl и / или C? - person deceze♦   schedule 25.05.2012East_Asian_Width=Ambiguous
. Я не знаю ни одной библиотеки Go, которая справлялась бы с этим так же, как библиотека Perl, описанная в моем ответе, но если есть такая вещь для Go, я бы хотел узнать об этом! Спасибо. - person tchrist   schedule 26.05.2012printf
заботится только о количестве байтов. Если вы хотите учесть количество символов, используйтеwprintf
(помните, он принимает форматwchar_t*
). В C нет функции форматирования, учитывающей ширину отображения. - person n. 1.8e9-where's-my-share m.   schedule 26.05.2012printf
. Возможно, будет лучше. - person n. 1.8e9-where's-my-share m.   schedule 26.05.2012printf
. Некоторые имеют дело с кодовыми точками. Ни один из них не касается ширины печати. - person tchrist   schedule 26.05.2012printf
определяется стандартом C, любое отклонение от него, ну, нестандартно. - person n. 1.8e9-where's-my-share m.   schedule 26.05.2012printf
определен стандартом C, никаким другим. Конечно, Perlprintf
не был бы настолько глуп, чтобы рассматривать символы как байты, и это не так. Ниprintf
в Ruby, ниprintf
в Java, ниfmt
в Go, ни%
в Python. Я уверен, что существует множество других языков с современной моделью обработки символов, которым не мешает побайтовое мышление. - person tchrist   schedule 27.05.2012