您的位置:首页 > 编程语言 > C语言/C++

C C++ 输出中文 【转】

2011-04-28 09:42 92 查看
2009-06-26 10:48
这几乎是每个人都会遇到的问题,因为我们是中国人,中国人编程不可避免的要输出汉字,哈哈,废话少说看看下面我转载的一篇文章 ,总结的还算全面。

这几天研究Unicode,勤快些,把看到的东西觉得有用的都copy了下来,文章是拼凑出来的,所以看起来会觉得有些乱 :)。

1. wprintf

Q : sizeof(wchar_t) = ?

A : 随编译器不同。(所以:在需要跨平台的时候尽量不用wchar_t) vc : sizeof(wchar_t) = 2;

Q: 在vc中,为什么直接使用wprintf(L"测试1234")会没有结果

A: 没有设置好locale,这样做

setlocale(LC_ALL ,"chs");

wprintf(L"%s",L"测试1234");

或者(假设当前活动codepage为chs)

char scp[16];

int cp = GetACP();

sprintf(scp,".%d",cp);

setlocale( LC_ALL, scp );

wprintf(L"测试1234");

2. wcout

一样,不过设定locale,请用std::locale

locale loc("chs");

wcout.imbue(loc);

wcout << L"测试1234" << endl;

这篇文章应该是[netsin]的成果,我勤快,记下来。

注:wprintf是C的标准库函数,但wcout不是C++的标准成员,C++中的 L"……" 是宽字符,却未必是unicode字符,这与编译器实现相关。

[乾坤一笑]
说:为什么 C/C++ 语言把 L"xx" 定义为由实现决定的呢?这显然是为了 C/C++ 的普适性、可移植性。Bjarne 的观点认为,C++
的方式是允许程序员使用任何字符集作为串的字符类型。另外,unicode 编码已经发展了若干版本了,是否能永久适合下去也不得而知。有关
unicode 的详细论述以及和其它字符集的比较,我推荐你看《无废话xml》。

以下两段代码的执行环境是 windows xp professional 英文版,编译器是 VS2005RTM。

// C

#include <stdio.h>

#include <locale.h>

int main( void )

{

setlocale( LC_ALL, "chs" );

//setlocale( LC_ALL, "Chinese-simplified" );

//setlocale( LC_ALL, "ZHI" );

//setlocale( LC_ALL, ".936" );

wprintf( L"中国" );

return 0;

}

// C++

#include <iostream>

#include <locale>

using namespace std;

int main( void )

{

locale loc( "chs" );

//locale loc( "Chinese-simplified" );

//locale loc( "ZHI" );

//locale loc( ".936" );

wcout.imbue( loc );

std::wcout << L"中国" << endl;

return 0;

}

说明:别混合使用 setlocale 和 std::locale 。

------------------------- 2006-07-05 记 -------------------------

"VC知识库" 编码为:56 43 D6 AA CA B6 BF E2 00 // ANSI编码

L"VC知识库" 在VC++ 中编码为:56 00 43 00 E5 77 C6 8B 93 5E 00 00 // (windows口中的unicode)编码

L"VC知识库" 在GCC(Dev-CPP4990) 中编码为:56 00 43 00 D6 00 AA 00 CA 00 B6 00 BF 00 E2 00 00 00 // 只是将ANSI编码简单的加0

L"VC知识库" 在GCC(Dev-CPP4992) 中编译失败,报 Illegal byte sequence

L"VC知识库" 在 Dev-CPP4992 中解决步骤为:

a. 将文件保存为 utf-8 编码 // utf-8 是unicode的其中一种,但和(windows口中的unicode)不一样

b. 去掉BOM头:用二进制编辑器(比如VC)去掉刚才utf-8文件的前三个字节 // Linux/UNIX并不使用BOM

c. 使用 gcc/g++ 编译运行

经过以上解决步骤,在 dev-cpp4992 中

"VC知识库" 编码为: 56 43 E7 9F A5 E8 AF 86 E5 BA 93 00 // utf-8编码,注意不再是ANSI编码了,因此用 printf/cout 将输出乱码

L"VC知识库" 编码为: 56 00 43 00 E5 77 C6 8B 93 5E 00 00 // (windows口中的unicode)编码

补充:在mingw32中使用wcout和wstring需要加一些宏,比如

#define _GLIBCXX_USE_WCHAR_T 1

#include <iostream>

int main( void )

{

std::wcout << 1 << std::endl;

}

可以编译通过,但无法Link通过,在网上google了一下,stlport说mingw32有问题,mingw32说是M$的c runtime有问题。

printf 、wprintf 在console下的unicode 输出

1. printf 只能提供ANSI/MB 的输出,不支持输出unicode stream.

例如:

wchar_t test[]=L"测试1234";

printf("%s",test);

是不会正确输出的

2.wprintf 同样不会提供unicode output,

但是他会把wchar_t的string转为locale的SB/MB字符编码,然后输出

例如:

wchar_t test[] = L"测试Test";

wprintf(L"%s",test);

会输出??1234之类的字符串,或者不输出任何结果

因为wprintf没有办法把L"测试Test"转为默认的ANSI,需要设置locale

setlocale(LC_ALL,"chs");

wchar_t test[] = L"测试Test";

wprintf(L"%s",test);

会有正确的输出

等同于printf("%ls",test);

综上: CRT I/O functions do not provide Unicode output.

3. Window console自从NT4就是一个真正的unicode console

不过输出unicode string,只有使用Windows API, WriteConsoleW

例如:

wchar_t test[] = L"测试1234";

DWORD ws;

WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),test,wcslen(test),&ws,NULL);

可以正确的输出而不需要设置locale,因为是真正的unicode的输出,跟codepage无关

4. 如何实现跨平台的console output

不要使用wchar_t和wprintf,因为这些都依赖于编译器.

ICU是IBM的一个成熟的跨平台支持unicode的libary,推荐使用

以下是ICU的uprintf实现

void uprintf(const UnicodeString &str) {

char *buf = 0;

int32_t len = str.length();

int32_t bufLen = len + 16;

int32_t actualLen;

buf = new char[bufLen + 1];

actualLen = str.extract(0, len, buf/*, bufLen*/); // Default codepage conversion

buf[actualLen] = 0;

printf("%s", buf);

delete buf;

}

它也是先把Unicode string转化为本地的codepage,然后printf,虽然也不是unicode output,但是跨平台,大多数情况会工作得很好。
后记:
mbstowcs(wchar_t *wcstr, const char *mbstr, size_t count )等函数第三个参数
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。
count的计算必须和mbstowcs在同一个locale下。

由一个评论的人说:

char scp[16];

int cp = GetACP();

sprintf(scp,".%d",cp);

setlocale( LC_ALL, scp );

wprintf(L"测试1234");

等同于

setlocale( LC_ALL, "" );

wprintf(L"测试1234");

附带一个我觉得挺幽默的关于unicode支持的评论

还是我来给你们上一课吧。首先按照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又少套了一只狼。

如果你在使用ms的p.j.先生的stl,那么你很幸运,因为你想要什么,微软都会给你的。大体上goodname的代码应该行的通。不过正如微软的座右
铭:给你需要的,但请不要看明白我是怎么做的。虽然微软公开stl源代码,但我宁愿自己从没有看过那个东西。本人甚至都在怀疑微软使用过什么给这些代码加
上1024位的密。使用微软的产品就像生活在Newyork一样:即在地狱又在天堂。

说到这里还没有说到重点,实际上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的转换,使用那个代码页管用,你就自己试试吧
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: