推荐给好友 上一篇 | 下一篇

C C++ 输出中文

n KZ[)?!n7A1. wprintfBSD爱好者乐园SZw*Y/x
Q : sizeof(wchar_t) = ?
EJ?m-vUV!WA : 随编译器不同。(所以:在需要跨平台的时候尽量不用wchar_t) vc : sizeof(wchar_t) = 2;BSD爱好者乐园"[&YzD/ckU"xVf
BSD爱好者乐园8f2vQT(z)KP:q
Q: 在vc中,为什么直接使用wprintf(L"测试1234")会没有结果BSD爱好者乐园i"f.l_9i#U
A: 没有设置好locale,这样做

%lG8t0[i9uT I D)E
setlocale(LC_ALL ,"chs");BSD爱好者乐园#P*n]do8e YK
wprintf(L
"%s",L"测试1234");
BSD爱好者乐园0k4Q5? ['\q2r ]s+C

BSD爱好者乐园,j4`%A*J`E-\
或者(假设当前活动codepage为chs)BSD爱好者乐园#N C&qf.G9o#q|5`R

charscp[16];
"h0e*f|(jvN.\!E*y
intcp=GetACP();
-b D6tp+L+Csprintf(scp,
".%d",cp);
w+NKl+o3w%`%`~tsetlocale( LC_ALL, scp );
}"HZ)l4EKwprintf(L
"测试1234");
BSD爱好者乐园;m"a y.e4gi(j

BSD爱好者乐园-Y)?_IPk)D
2. wcout
L:U,Fo)?y一样,不过设定locale,请用std::locale

xbw,I?1R#F)L
     locale loc("chs");BSD爱好者乐园'd;`iRH
     wcout.imbue(loc);BSD爱好者乐园 RG ~f;@? I
     wcout
<<L"测试1234"<<endl;
^U q9|7h!mi!y

{)h4KJ(e;c0pr(Y{ Nz 

7fg7Ro!ixBSD爱好者乐园,Nl? wf-?

这篇文章应该是[netsin]的成果,我勤快,记下来。BSD爱好者乐园[`,N3L.d m
注:wprintf是C的标准库函数,但wcout不是C++的标准成员,C++中的 L"……" 是宽字符,却未必是unicode字符,这与编译器实现相关。
cg/L\&tL[乾坤一笑]说:为什么 C/C++ 语言把 L"xx" 定义为由实现决定的呢?这显然是为了 C/C++ 的普适性、可移植性。Bjarne 的观点认为,C++ 的方式是允许程序员使用任何字符集作为串的字符类型。另外,unicode 编码已经发展了若干版本了,是否能永久适合下去也不得而知。有关 unicode 的详细论述以及和其它字符集的比较,我推荐你看《无废话xml》。

m sz#f U8cBSD爱好者乐园g7st3RYd2Q.p

BSD爱好者乐园3q(e2X5? N(_
以下两段代码的执行环境是 windows xp professional 英文版,编译器是 VS2005RTM。BSD爱好者乐园#W)OLqF'_$c | MD

t Bs\r7{ t// C
}w&_U(?!p r{Vl#include <stdio.h>
u| a0L#e&qMX2K#include <locale.h>BSD爱好者乐园{*k5f3g\!`/S$R
int main( void )
'?0I(``q{
k[ M"{ rb.NS    setlocale( LC_ALL, "chs" );BSD爱好者乐园 {wcH Dx Y*k
    //setlocale( LC_ALL, "Chinese-simplified" );BSD爱好者乐园wD&O~8v"mY q
    //setlocale( LC_ALL, "ZHI" );BSD爱好者乐园 I%uJ"g P3a7UH
    //setlocale( LC_ALL, ".936" );
eD8h9y$l'A E    wprintf( L"中国" );

$IF&h;s1pAYBSD爱好者乐园q&hWD|^N

    return 0;BSD爱好者乐园/gwE:_LP7b
}

/c1wVPP)}8N|3h.kBSD爱好者乐园$T }1qL'e![uRH t.]

// C++BSD爱好者乐园9m0jW-DZmO v
#include <iostream>
g XY:Ni aI7lM#include <locale>
,@8J.d}wxo-n0HA!b$pusing namespace std;
]&A_ Wn&o']4O7x;f/BZint main( void )BSD爱好者乐园BL V G%w~6sqF
{
5a'J#q]:F'j H    locale loc( "chs" );BSD爱好者乐园7@:DzL9n dp&LK
    //locale loc( "Chinese-simplified" );
gJG7@V0H(g s    //locale loc( "ZHI" );BSD爱好者乐园Go:u|2BO5f
    //locale loc( ".936" );BSD爱好者乐园"c7VJ-EN,o,`%b'} ` G AI
    wcout.imbue( loc );
PQ"olAF3Pw'D    std::wcout << L"中国" << endl;

\w x.C2co?|6cq.m&J

{5y$G'G{    return 0;BSD爱好者乐园&K.I7A_x]L
}BSD爱好者乐园&G)@E,vZk

BSD爱好者乐园1qt3ztYrn

说明:别混合使用 setlocale 和 std::locale 。BSD爱好者乐园u6V2@5E"}$F5j NWdw

x };t x r/la(B$i-------------------------2006-07-05 记-------------------------BSD爱好者乐园1_S?!UYh

BSD爱好者乐园)@;Et(_iv+ZC1C

"VC知识库"                        编码为:56 43 D6 AA CA B6 BF E2 00                            // ANSI编码
.T3s~n1T;U(iL"VC知识库" 在VC++               中编码为:56 00 43 00 E5 77 C6 8B 93 5E 00 00                   // (windows口中的unicode)编码
&VH @RH^,XPL"VC知识库" 在GCC(Dev-CPP4990) 中编码为:56 00 43 00 D6 00 AA 00 CA 00 B6 00 BF 00 E2 00 00 00 // 只是将ANSI编码简单的加0BSD爱好者乐园!OD8zo3m]B
L"VC知识库" 在GCC(Dev-CPP4992) 中编译失败,报 Illegal byte sequenceBSD爱好者乐园 X/G1p2t(I:Uav[S

q1B4M9M&V Oy)yL"VC知识库" 在 Dev-CPP4992 中解决步骤为:BSD爱好者乐园p.kEc+L6o([7C5]'p
a. 将文件保存为 utf-8 编码                                          // utf-8 是unicode的其中一种,但和(windows口中的unicode)不一样BSD爱好者乐园WQG/H f*}'W
b. 去掉BOM头:用二进制编辑器(比如VC)去掉刚才utf-8文件的前三个字节 // Linux/UNIX并不使用BOM
2X8M,U,C:@*u2Lqc. 使用 gcc/g++ 编译运行

5}_Ky$AKBSD爱好者乐园+k~,V:Xs?f'T4AD

经过以上解决步骤,在 dev-cpp4992 中
[F3Ac^0c W2l"VC知识库" 编码为: 56 43 E7 9F A5 E8 AF 86 E5 BA 93 00 // utf-8编码,注意不再是ANSI编码了,因此用 printf/cout 将输出乱码BSD爱好者乐园] sb?y `&_'ws
L"VC知识库" 编码为: 56 00 43 00 E5 77 C6 8B 93 5E 00 00 // (windows口中的unicode)编码BSD爱好者乐园VCS};x3@,d

BSD爱好者乐园9WwN!^2U:f^Q

补充:在mingw32中使用wcout和wstring需要加一些宏,比如BSD爱好者乐园.mhr^![_;o a7^
#define _GLIBCXX_USE_WCHAR_T 1BSD爱好者乐园n5s[Ktg
#include <iostream>BSD爱好者乐园!^hQK b:_7jq K
int main( void )
7]7f(l#zq{
V2zm-S+tD    std::wcout << 1 << std::endl;BSD爱好者乐园\,kQ-Qp `3^n%c"N\
}BSD爱好者乐园at%Hl'y$b0~
可以编译通过,但无法Link通过,在网上google了一下,stlport说mingw32有问题,mingw32说是M$的c runtime有问题。BSD爱好者乐园Y+O/yC\ O[)G6V

BSD爱好者乐园H[LpE(QY6t \ H

 BSD爱好者乐园nx h @4AFi

BSD爱好者乐园wEivD5l6~

 

){MK,HcO%S;Y"cY

printf   、wprintf 在console下的unicode 输出

1. printf 只能提供ANSI/MB 的输出,不支持输出unicode stream.
7gF7K6{8r"A:`G例如:
wchar_t test[]=L"测试1234";
#L`LC1t4V{ nMKprintf(
"%s",test);
是不会正确输出的
dO6JpYl$T
7O*`3D0Xh@!T{
2H(]Ot.o&AN2.wprintf 同样不会提供unicode output,
+S9G4L2a SF Q   但是他会把wchar_t的string转为locale的SB/MB字符编码,然后输出BSD爱好者乐园~'L6v#qhW h
例如:
wchar_t test[]=L"测试Test";BSD爱好者乐园;em5\i5um
wprintf(L
"%s",test);
会输出??1234之类的字符串,或者不输出任何结果BSD爱好者乐园 H1{lgn c2`
因为wprintf没有办法把L"测试Test"转为默认的ANSI,需要设置locale
setlocale(LC_ALL,"chs");BSD爱好者乐园r/U%HI&Z4VC
wchar_t test[]
=L"测试Test";
2Kf1z$EQ#TS}"qwprintf(L
"%s",test);
会有正确的输出BSD爱好者乐园Yu:A/]-S3|R
等同于printf("%ls",test);
y5bD2q0Lw1^el
z"e+t`"l9?m X#o6Gx综上:CRT I/O functions do not provide Unicode output.
5l1s%R4o-e$Er:yGBSD爱好者乐园gh2N&z*oR|'B
3. Window console自从NT4就是一个真正的unicode console
ta[N6z@不过输出unicode string,只有使用Windows API, WriteConsoleW
UX G&b#^2y2c,nF例如:BSD爱好者乐园/H0K"k A_Y.I-v
wchar_t test[]=L"测试1234";
1MKb7Z6j2[sDWORD ws;BSD爱好者乐园3xWV.s`*K2f:w [
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),test,wcslen(test),
&ws,NULL);
可以正确的输出而不需要设置locale,因为是真正的unicode的输出,跟codepage无关
ZWF@#Rpb
&r t Jiu}4. 如何实现跨平台的console outputBSD爱好者乐园jI f(R mp
    不要使用wchar_t和wprintf,因为这些都依赖于编译器.
Ei9B#y3j+c1J     ICU是IBM的一个成熟的跨平台支持unicode的libary,推荐使用BSD爱好者乐园(LJ5M4?{4n

9o P.U.x@8l$kzH以下是ICU的uprintf实现
voiduprintf(constUnicodeString&str) {
K;Czx8o6q    
char*buf=0;BSD爱好者乐园$XnT2gZ$|!dx^5qin
     int32_t len
=str.length();BSD爱好者乐园U$U,d&O QrHN
     int32_t bufLen
=len+16;
D}XXaM     int32_t actualLen;BSD爱好者乐园7S9pjl)XWd
     buf
=newchar[bufLen+1];
1z6nI5sX!R1U#U     actualLen
=str.extract(0, len, buf/*, bufLen*/);//Default codepage conversion
|._+H$Uz9p
     buf[actualLen]=0;BSD爱好者乐园+Du Bh"rB8G&h'f
     printf(
"%s", buf);
(H3N]m3q;L     delete buf;BSD爱好者乐园W h-Og_J2L KJ,^6J
}
它也是先把Unicode string转化为本地的codepage,然后printf,虽然也不是unicode output,但是跨平台,大多数情况会工作得很好。
后记:
mbstowcs(wchar_t *wcstr, const char *mbstr, size_tcount)等函数第三个参数
count:   The maximum number of multibyte characters to convert.
指待转换的多字节字符串相对于目前活动locale的字符个数加一。
比如:字符串"abc赵123"对于C locale而言count是strlen("abc赵123"),即8+1。
            而对于chinese-simplified.936而言count就是7+1。

"I!sv$P BTPoH?:P8~count的计算必须和mbstowcs在同一个locale下。

r-E;?#[a]tk%}5_BSD爱好者乐园 xrQ&E{!jC3sx

由一个评论的人说:BSD爱好者乐园/Y$|/P!X S]i~ ve

BSD爱好者乐园8XC)T g.W(M)n

char scp[16];
8fBcSP*_'z9lint cp = GetACP();
pV&_*Q6Wsprintf(scp,".%d",cp);
H8A)g*?2L1uyrsetlocale( LC_ALL, scp );BSD爱好者乐园"EG4fm+Ra y[$^
wprintf(L"测试1234");BSD爱好者乐园J"?7H8yB4{ M4G#zI
等同于
5e"OQDnf4S-e ysetlocale( LC_ALL, "" );BSD爱好者乐园t4m(p6M.B F\*F OI
wprintf(L"测试1234");

szo\Dc

(Gw+f&S9Y'fCYDF附带一个我觉得挺幽默的关于unicode支持的评论BSD爱好者乐园;@]5k,[9b,\#o{

7R-Q,{;T p`还是我来给你们上一课吧。首先按照c99支持unicode字符集,这里指内存中的字符编码,c++当然应该支持,这是毫无疑问的。但是unicode的外部表示如utf7,utf8,utf8n,utf16le,utf16be等等外部存储格式,c++委员会显然没有理由理会这些东西,作为世界上最帅的stl库sgi-stl3.3外带的IO流库认为自己没有义务实现各种各样的字符编码的转换。事实上这样做会导致stl-io库对操作系统的严格区分,结果必然是舍了一群孩子才套了一只狼。所以,sgi-stl声明除了标准的“C”别的一概不予理会。结果是如果我们使用sgi-stl而又不提供新的诸如c_local_stub_win32.cxx这样的基于windows的c-locale底层接口的实现,像goodname那样用什么locale都不管用。说到这里,如果你在使用stl-port,stl-port4.6比sgi-stl的野心大多了,孩子肯定是舍了,狼大约也没套多。我并没有做过stl-port4.6使用其他locale的例子,但我想,他的表现大约会像微软的p.j.先生的stl一样帅吧。bcb6,cbx部署的stl库恰好就是stl-port的某个版本,如果按照goodname的办法不能成功,那么很不幸stlport又少套了一只狼。
#J.Nr&@5p2[如果你在使用ms的p.j.先生的stl,那么你很幸运,因为你想要什么,微软都会给你的。大体上goodname的代码应该行的通。不过正如微软的座右铭:给你需要的,但请不要看明白我是怎么做的。虽然微软公开stl源代码,但我宁愿自己从没有看过那个东西。本人甚至都在怀疑微软使用过什么给这些代码加上1024位的密。使用微软的产品就像生活在Newyork一样:即在地狱又在天堂。BSD爱好者乐园qR_1p'~ Tc
说到这里还没有说到重点,实际上stl-io流在处理内部字符流和外部数据流间的互转换时,使用自带的locale中指定的codecvt*对象,其中的do_in,和do_out实现转换,按照sgi-stl的实现,“c”完成内部的wchar_t和外部的char转换时不会考虑10646更不会考虑其他任何形式的mbcs,事实上c++标准根本就不认识这些东西。所以当你想要输出汉字时,每个汉字的高位都被丢弃,很不爽吧,解决的办法就是继承一个codecvt重写do_in,do_out在里面用psdk的功能完成unicode到mbcs的转换,使用那个代码页管用,你就自己试试吧

!O^IF#l%}&S K

[版权声明]BSD爱好者乐园站内文章,如来源不是互联网,则均系原创或翻译之作,可随意转载,或以此为基础进行演译,但务必以链接形式注明原始出处和作者信息,否则属于侵权行为。另对本站转载他处文章,俱有说明,如有侵权请联系本人,本人将会在第一时间删除侵权文章。
TAG: 中文
 

评分:0

我来说两句

seccode