初探C++ Builder 2009的UnicodeString

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

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

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


初探C++ Builder 2009的UnicodeString_第1张图片

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

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



初探C++ Builder 2009的UnicodeString_第2张图片


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

初探C++ Builder 2009的UnicodeString_第3张图片

        简体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)了。

初探C++ Builder 2009的UnicodeString_第4张图片

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

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

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


初探C++ Builder 2009的UnicodeString_第5张图片

        结果肯定是乱码,繁体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( 950 0 , 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  }



运行一下看看:

初探C++ Builder 2009的UnicodeString_第6张图片



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

 

                                                                             此文章来自Tuuzed(土仔)作者

                                                                             

你可能感兴趣的:(初探C++ Builder 2009的UnicodeString)