2012-05-25 13 views
5

जब मैं पर्ल या सी का उपयोग कुछ डेटा printf, मैं प्रत्येक स्तंभ की चौड़ाई को नियंत्रित करने के उनके स्वरूप की कोशिश की,UTF-8 चीनी वर्ण

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

तरह की चौड़ाई प्रदर्शन जारी करना लेकिन जब str चीनी वर्ण है, तो स्तंभ अपेक्षित के रूप में संरेखित नहीं करता है। अटैचमेंट चित्र देखें।

मेरा उबंटू का वर्णमाला एन्कोडिंग zh_CN.utf8 है, जहां तक ​​मुझे पता है, utf-8 एन्कोडिंग में बाइट्स की 1 ~ 4 लंबाई है। चीनी चरित्र में 3 बाइट हैं। मेरे परीक्षण में, मैंने पाया कि printf का स्वरूप नियंत्रण एक चीनी चरित्र को 3 के रूप में गिनता है, लेकिन यह वास्तव में 2 एसीआई चौड़ाई के रूप में प्रदर्शित होता है।

तो असली प्रदर्शन चौड़ाई एक निरंतर अपेक्षा के अनुरूप है, लेकिन चीनी चरित्र की संख्या से संबंधित एक चर, यानी

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

डब्ल्यू चौड़ाई सीमा की उम्मीद है, एक्स चीनी अक्षरों की गिनती, तैर नहीं है (एक्स) वास्तविक प्रदर्शन चौड़ाई है।

तो अधिक चीनी चरित्र str में, छोटा यह प्रदर्शित करता है।

मैं जो चाहता हूं उसे प्राप्त कैसे कर सकता हूं? Printf से पहले चीनी पात्रों की गणना करें?

जहां तक ​​मुझे पता है, सभी चीनी या यहां तक ​​कि सभी विस्तृत वर्ण मुझे लगता है, 2 चौड़ाई के रूप में प्रदर्शित होता है, तो printf इसे 3 के रूप में क्यों मानता है? यूटीएफ -8 के एन्कोडिंग में प्रदर्शन की लंबाई के साथ कुछ लेना देना नहीं है।

+0

दूसरे शब्दों में, आप पर्ल और/या सी के लिए 'printf' के बहुविविध-जागरूक संस्करण की तलाश में हैं? – deceze

+0

मैंने सी में utf8 डिकोडिंग कभी नहीं किया है लेकिन यहां एक गो कोड है जो एक utf-8 स्ट्रिंग में रनों की गणना करता है: http://golang.org/src/pkg/unicode/utf8/utf8.go?s=4824:4876 # एल 202 –

+1

@dystroy यह केवल कोड बिंदुओं (यानी, रन) की गणना करने की बात नहीं है। इसके बजाय, यह ध्यान में रख रहा है कि विभिन्न कोड बिंदु प्रति UAX # 11 प्रति 0, 1, या 2 प्रिंट कॉलम का प्रतिनिधित्व करते हैं, और यह काफी सूक्ष्म है, खासकर 'East_Asian_Width = अस्पष्ट' वर्णों के साथ। मुझे किसी भी गो लाइब्रेरी के बारे में पता नहीं है, जिस तरह से मेरे उत्तर में पर्ल लाइब्रेरी का वर्णन किया गया है, लेकिन यदि गो के लिए ऐसी कोई चीज है, तो मुझे इसके बारे में जानना अच्छा लगेगा! धन्यवाद। – tchrist

उत्तर

6

हां, यह printf के सभी संस्करणों के साथ एक समस्या है जिसे मैं जानता हूं। मैं संक्षेप में इस मामले पर this answer और this one में चर्चा करता हूं।

सी के लिए, मुझे पुस्तकालय की जानकारी नहीं है जो आपके लिए यह करेगी, लेकिन अगर किसी के पास यह है, तो यह आईसीयू होगा।

पर्ल के लिए, आपको Unicode::GCString मॉड्यूल फॉर्म सीपीएएन का उपयोग प्रिंट कॉलम की संख्या की गणना करने के लिए एक यूनिकोड स्ट्रिंग लेना होगा। यह Unicode Standard Annex #11: East Asian Width खाते में आता है।

उदाहरण के लिए, कुछ कोड बिंदु 1 कॉलम लेते हैं और अन्य 2 कॉलम लेते हैं। ऐसे कुछ भी हैं जो पात्रों और अदृश्य नियंत्रण वर्णों को संयोजित करने की तरह कोई कॉलम नहीं लेते हैं। कक्षा में columns विधि है जो स्ट्रिंग को कितनी कॉलम लेती है।

मेरे पास यूनिकोड टेक्स्ट लंबवत here संरेखित करने के लिए इसका उपयोग करने का एक उदाहरण है। यह यूनिकोड स्ट्रिंग्स का एक गुच्छा सॉर्ट करेगा, जिसमें कुछ वर्ण और "चौड़े" एशियाई विचारधारा (सीजेके पात्र) संयोजन शामिल हैं, और आपको चीजों को लंबवत रूप से संरेखित करने की अनुमति देते हैं। थोड़ा umenu डेमो प्रोग्राम है जो प्रिंट कि अच्छी तरह से उत्पादन गठबंधन, नीचे शामिल किया गया है के लिए

sample terminal output

कोड।

आपको शायद अधिक महत्वाकांक्षी Unicode::LineBreak मॉड्यूल में रुचि हो सकती है, जिसमें उपर्युक्त Unicode::GCString कक्षा केवल एक छोटा घटक है। यह मॉड्यूल बहुत कूलर है, और Unicode Standard Annex #14: Unicode Line Breaking Algorithm खाते में आता है।

पर्ल v5 पर परीक्षण किए गए छोटे umenu डेमो के लिए कोड यहां दिया गया है।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 बहुत कम सुविधाजनक है, लेकिन कम से कम यह संभव है।

संबंधित मुद्दे