悲情土仔一生

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  14 随笔 :: 0 文章 :: 74 评论 :: 0 Trackbacks

  初探C++ Builder 2009的UnicodeString


作者:Tuuzed(土仔)   发表于:2009年6月25日23:39:34
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
http://www.cppblog.com/tuuzed/archive/2009/06/26/88538.html



       下面两个问题一直困扰着我这个处于第1层的菜鸟:1、为什么卡巴斯基KIS2009简体中文版安装在繁体XP下能正常显示简体中文,而瑞星、金山等则是乱码?2、为什么简体的MS Word或Firefox能显示繁体中文(特指Big5编码)的内容?处于1层以上的程序员(见《程序员》6月杂志周伟明的《程序员的十层楼》)肯定已经笑出来了,请略过剩下的内容。。。

        这么说吧,如果想实现一个可以在简体中文系统中显示繁体中文的“记事本”,C++ Builder2009中如何实现?很多人(包括我)在没有了解各种字符编码以前,想当然地觉得既然支持Unicode了,那么直接使用TMemo的LoadFromFile方法直接load一个繁体中文的文档就能显示了。因为支持Unicode了嘛,Unicode就是在任何系统都能显示正常。好像很对,先试一下,初探嘛。

        一、不变的“简体中文”版
        既然支持Unicode,搞一个Form,放一个Button在上面,



 
      Caption先不要去动,用程序去修改:

      Form1->Caption = "简体中文";
       Button1->Caption = "汉字";





       简体XP下显示正常,可繁体XP下就显示乱码了。不是说支持Unicode了吗?跟踪调试一下:



        简体XP下,“汉字”的“汉”编码是0x6C49,而繁体XP下,“汉”的编码变成了0x7296。我们知道“汉”在Uunicode编码中是0x6C49,说明简体XP下是正确的,而繁体XP下就不正确了。为什么会有这样的情况发生?把断点设置在“Button1->Caption = "汉字";”这一句,再用F7一直跟踪。原来是CB2009在把“汉字”赋值给Button1->Caption前,先进行了Ansi到Unicode的转换,恰恰问题就在CB2009使用的这个函数:InternalUStrFromPCharLen(Dest, Source, Length, DefaultSystemCodePage);函数在实现过程中获取了系统默认的CodePage(http://www.cppblog.com/shenhuafeng/archive/2007/04/05/21336.html),而简体XP和繁体XP的CodePage不一样(一个为936,一个为950)导致在转换到UNICODE的时候结果不一致,也就导致到繁体XP下显示为乱码。如何解决呢?在CB2009的帮助“Unicode in RAD Studio”一章中的“Issues(问题)”节中提到:运用“U”这个标量(一个宏,与VC++中的“L”类似)将ANSI字符常量强制识别为Unicode。这个过程是在编译时就已经完成,编译的时候是在简体XP下,所以程序运行时内存中存储的“汉字”Unicode是正确的。为了证实,将代码变为Button1->Caption = U"汉字";,再用F7跟踪。结果是程序一运行到这,就马上用UnicodeSetLength(var dst: UnicodeString; len: Integer);(注意dst的数据类型)来初始化一个UnicodeString类,等着给TButton赋值(TControl.SetText)了。



        现在终于明白,不变的“简体中文”其实是不变的Unicode编码,已经不是我们的GB了。那么CB2009中的UnicodeString默认的CodePage是啥?调用UnicodeString.CodePage()就知道了——1200。


        二、正确显示“繁体中文”

        简体XP下显示繁体好像都很容易:用IE、Firefox浏览繁体网站,用MS Word打开繁体内容doc文档等。如何用CB2009也实现相应功能?先试试用Memo控件来Load一个繁体文本看看:




        结果肯定是乱码,繁体XP下运行这个程序是能正常显示的。Memo控件中的每一行其实都是UnicodeString(属性Lines是TStrings类的对象),而繁体内容的TXT文本按ANSI保存,在Memo载入文件的时候做了一个ANSI到Unicode的转换。有了之前的跟踪结果,可以想象CB2009是获取了系统的默认CodePage(936)而导致繁体不能正确转换为UTF-16。那么我们让CB2009重新进行CodePage950的转换就应该可以正确显示了。

        有个函数在前面跟踪源码的时候出现过——MultiByteToWideChar,看名字很容易理解它的作用是把多字节转为宽字符,CB2009应该是利用了这个函数将ANSI进行了转换,当然CB2009是用的简体系统默认的CodePage。转换应该可逆,那么应该有WideCharToMultiByte。实现它看看:

 1 UnicodeString __fastcall BIG5ToUnicode(UnicodeString usString)
 2 {
 3     if (GetOEMCP() == 950
 4     {
 5         //如果为繁体系统,不用转换
 6         return usString;                                                
 7     }
 8     //预分配空间
 9     int length = usString.Length() * 2;                    
10     char *chBuffer = new char[length+1];
11     //按系统默认的codepage转回去
12     int iReturn = WideCharToMultiByte(GetOEMCP(), 0, usString.w_str(), -1, chBuffer, length+1, NULL, NULL);
13     wchar_t *wcBuffer = new wchar_t[iReturn+1];
14     //按Big5编码转换回来
15     iReturn = MultiByteToWideChar(9500, chBuffer, -1, wcBuffer, iReturn+1 );
16     usString = UnicodeString(wcBuffer);
17     delete chBuffer;
18     delete wcBuffer;
19     return usString;
20 }



“载入文件”的Click事件实现如下:

 1 void __fastcall TForm1::btn1Click(TObject *Sender)
 2 {
 3     TStringList *slBuf = new TStringList();
 4     slBuf->LoadFromFile("d:\\eula.txt");
 5     int iCount = slBuf->Count;
 6     for (int i = 0; i < iCount; i++)
 7     {
 8         mmo1->Lines->Add(BIG5ToUnicode(slBuf->Strings[i]));
 9         Application->ProcessMessages();
10     }
11 }



运行一下看看:





        好像自己的两个问题有了答案,但是总觉得第二个问题的方法效率低下,毕竟又转了一道。肯定还有更好的方法,当然,在没有找到好的办法前,我们这些菜鸟用用这种方法也是可以的。毕竟成长的过程是痛苦的。

posted on 2009-06-26 00:41 土仔 阅读(7075) 评论(5)  编辑 收藏 引用 所属分类: 土仔编程

评论

# re: 初探C++ Builder 2009的UnicodeString 2009-06-26 13:12 88
不错的文章。  回复  更多评论
  

# re: 初探C++ Builder 2009的UnicodeString 2009-06-26 18:54 buffer
偶也是一个菜鸟~

学到了一直没想明白的问题,谢谢~  回复  更多评论
  

# re: 初探C++ Builder 2009的UnicodeString[未登录] 2009-06-27 16:54 五哥
有没有想过,字符常量不是unicode是因为源代码文件(.cpp)不是以unicode存的,把文件另存为utf-8或utf-16就应该没问题了.没用过c++ build不知道IDE和编译器支不支持unicode的源文件。  回复  更多评论
  

# re: 初探C++ Builder 2009的UnicodeString 2009-06-29 22:41 土仔
@五哥
没错,我也发现了IDE有这样的支持,再研究看看。多谢!  回复  更多评论
  

# re: 初探C++ Builder 2009的UnicodeString 2016-01-18 16:19 郑俊
太牛啦,学习学习了。找了2天才找到你的帖子,解决我的燃眉之急  回复  更多评论
  


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理