Sí, este es un problema con todas las versiones de printf
que yo sepa. Discuto brevemente el asunto en this answer y también en this one.
Para C, no sé de una biblioteca que haga esto por usted, pero si alguien la tiene, sería ICU.
Para Perl, debe usar el módulo Unicode::GCString de CPAN para calcular el número de columnas de impresión que tomará una cadena Unicode. Esto tiene en cuenta Unicode Standard Annex #11: East Asian Width.
Por ejemplo, algunos puntos de código ocupan 1 columna y otros ocupan 2 columnas. Incluso hay algunos que no ocupan columnas en absoluto, como la combinación de caracteres y caracteres de control invisibles. La clase tiene un método columns
que devuelve la cantidad de columnas que ocupa la cadena.
Tengo un ejemplo de cómo usar esto para alinear texto Unicode verticalmente here. Clasificará un conjunto de cadenas Unicode, incluidas algunas con la combinación de caracteres e ideogramas asiáticos "anchos" (caracteres CJK), y le permitirá alinear las cosas verticalmente.
Código
para el pequeño programa de demostración umenu
que imprime esa salida muy bien alineados, se incluye a continuación.
También le puede interesar el módulo Unicode::LineBreak, mucho más ambicioso, de los cuales la clase Unicode::GCString
mencionada anteriormente es solo un componente más pequeño. Este módulo es mucho más fresco, y tiene en cuenta Unicode Standard Annex #14: Unicode Line Breaking Algorithm.
Aquí está el código para la pequeña demostración umenu
, probado en 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;
}
Como se ve, la clave para hacer que funcione en ese programa en particular es esta línea de código, que sólo llama a otras funciones definidas anteriormente, y utiliza el módulo estaba discutiendo:
print pad(entitle($item), $width, ".");
Eso rellenará el artículo con el ancho dado usando puntos como el carácter de relleno.
Sí, es mucho menos conveniente que printf
, pero al menos es posible.
En otras palabras, ¿está buscando una versión multibyte de 'printf' para Perl y/o C? – deceze
Nunca he hecho la decodificación utf8 en C, pero aquí hay un código Go que cuenta las runas en una cadena utf-8: http://golang.org/src/pkg/unicode/utf8/utf8.go?s=4824:4876 # L202 –
@dystroy No se trata solo de contar los puntos de código (es decir, runas). Por el contrario, se tiene en cuenta que diferentes puntos de código representan 0, 1 o 2 columnas de impresión por UAX # 11, y esto es bastante sutil, especialmente con los caracteres 'East_Asian_Width = Ambiguous'. No conozco ninguna biblioteca de Go que trate esto como lo describió la biblioteca de Perl en mi respuesta, pero si existe algo para Go, ¡me encantaría aprender sobre esto! Gracias. – tchrist