Sì, questo è un problema con tutte le versioni di printf
di cui sono a conoscenza. Parlo brevemente della questione in this answer e anche in this one.
Per C, non conosco una libreria che farà questo per voi, ma se qualcuno ce l'ha, sarebbe ICU.
Per Perl, è necessario utilizzare il modulo CPAN Unicode::GCString per calcolare il numero di colonne di stampa occupate da una stringa Unicode. Questo prende in considerazione Unicode Standard Annex #11: East Asian Width.
Ad esempio, alcuni punti di codice occupano 1 colonna e altri occupano 2 colonne. Ci sono anche alcuni che non occupano affatto le colonne, come combinare caratteri e caratteri di controllo invisibili. La classe ha un metodo columns
che restituisce quante colonne occupa la stringa.
Ho un esempio di utilizzo di questo per allineare il testo Unicode verticalmente here. Analizzerà un gruppo di stringhe Unicode, incluse alcune con la combinazione di caratteri e ideogrammi asiatici "larghi" (caratteri CJK) e consentirà di allineare le cose verticalmente.
Codice per il piccolo programma umenu
demo che le stampe che ben allineati in uscita, è incluso di seguito.
Potresti anche essere interessato al modulo molto più ambizioso Unicode::LineBreak, di cui la classe Unicode::GCString
sopra menzionata è solo un componente più piccolo. Questo modulo è molto più interessante e prende in considerazione Unicode Standard Annex #14: Unicode Line Breaking Algorithm.
Ecco il codice per la piccola demo umenu
, testata su 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;
}
Come si vede, la chiave per farlo funzionare in quel particolare programma è questa riga di codice, che ha appena chiama altre funzioni definite sopra, e utilizza il modulo stavo discutendo:
print pad(entitle($item), $width, ".");
Questo riempirà l'elemento della larghezza specificata usando i punti come carattere di riempimento.
Sì, è molto meno conveniente che printf
, ma almeno è possibile.
In altre parole, stai cercando una versione multipla di 'printf' per Perl e/o C? – deceze
Non ho mai eseguito la decodifica utf8 in C ma ecco un codice Go che conta le rune in una stringa utf-8: http://golang.org/src/pkg/unicode/utf8/utf8.go?s=4824:4876 # L202 –
@dystroy Non si tratta solo di contare i punti del codice (es. Rune). Piuttosto, si tiene conto che diversi punti di codice rappresentano 0, 1 o 2 colonne di stampa per UAX # 11, e questo è abbastanza sottile, specialmente con i caratteri 'East_Asian_Width = Ambiguous'. Non conosco alcuna libreria Go che tratti questo come fa la libreria Perl descritta nella mia risposta, ma se c'è una cosa del genere per Go, mi piacerebbe conoscerla! Grazie. – tchrist