CEGUI::String类分析

简介: CEGUI::String类算是一个不错的类,但是说实话,这个类让非英文国度的IT们不是很舒服。比如,自我们学习C++编程以来,我们一直使用这样的输出语句: std::cout

CEGUI::String类算是一个不错的类,但是说实话,这个类让非英文国度的IT们不是很舒服。比如,自我们学习C++编程以来,我们一直使用这样的输出语句:

std::cout << "名字: " << szName << std::endl;

其中"名字:"使用的是多字节编码(ASCII编码的扩充版本)。我们的控制台能正确显示这些中文文本。但是当我们初次了解并使用CEGUI的时候,却无法使用这样简单的功能:

CEGUI::String strName = "徐杰";
pNameWindow->setText( strName );

不是说编译不过,而是显示不正确。求其根源有两个原因:

  1. CEGUI 使用 utf32 编码方案,这个并不重要。重要的是 CEGUI::String 如何将 const char* 这样参数转换成其内部编码方式。只要转换正确,CEGUI会按照自己的方式,将utf32 编码方案的文本显示出来。但不幸的是,eddy博士的团队并不认为 const char* 会指向非英文字符串,所以 CEGUI::String 接受 const char* 的构造函数,根本就没有进行多字节编码到 utf32 编码的转换。

  2. 其次,即使我们将多字节编码正确转换成了 utf32 编码,传给了CEGUI::String,我们也无法让空间显示出中文。这个问题主要是字体文件的问题,正确的字符编码必须使用正确字体文件,才能正确显示出中文。(一开始,我还以为是CEGUI::String本身的问题,后来到网上查了查,CEGUI::String的utf8构造函数是没有问题的,只是需要正确的字体文件)

我们根据这两个问题,分别给出解决方案:

  1. CEGUI::String 接受几种字符串类型,通过查看源码CEGUI::String只支持其他三种字符串类型:

    String(const char* chars, size_type chars_len)
    	{
    		init();
    		assign(chars, chars_len);
    	}
    String(const std::string& std_str, size_type str_idx, size_type str_num = npos)
    	{
    		init();
    		assign(std_str, str_idx, str_num);
    	}
    String(const utf8* utf8_str, size_type chars_len)
    	{
    		init();
    		assign(utf8_str, chars_len);
    	}
    第一种和第二种其实是一样的,std::string只不过是 const char* 的封装类,内部还是使用的多字节编码。第三种是接受一个utf8编码的字符串。之前我们讲过,CEGUI::String 根本不对 const char* 进行适当的编码转换,所以如果我们想传入中文文本,那么,我们唯一的出路是使用 utf8 编码的字符串。这样我们就需要构造 utf8 字符串了。Window SDK有响应的函数可以帮助我们完成多字节编码到utf8编码的转换。下面是我写的转换函数:
    #define MB2UTF8( str ) ( const CEGUI::utf8* )( MultiByteToUtf8( ( str ) ).c_str() )
    
    std::string MultiByteToUtf8( const char* pszMultiByte )
    {
        std::string strUtf8;
    
        if( NULL == pszMultiByte )
        {
            return strUtf8;
        }
    
        int iNumCharacter = 0;
    
        //convert from MultiByte to WideChar
        std::wstring wcsText;
        iNumCharacter = MultiByteToWideChar( CP_ACP, 0, pszMultiByte, -1, NULL, 0 );
        wcsText.resize( iNumCharacter + 1 );//addition 1 for '\0'
    
        MultiByteToWideChar( CP_ACP, 0, pszMultiByte, -1, &wcsText[ 0 ], iNumCharacter );
    
        //convert from WideChar to utf8
        iNumCharacter = WideCharToMultiByte( CP_UTF8, 0, &wcsText[ 0 ], -1, NULL, 0, NULL, NULL );
        strUtf8.resize( iNumCharacter + 1 );
        WideCharToMultiByte( CP_UTF8, 0, &wcsText[ 0 ], -1, &strUtf8[ 0 ], iNumCharacter, NULL, NULL );
    
        return strUtf8;
    }
    
    void setWindowText( CEGUI::Window* pWindow, const char* pszText )
    {
        if( NULL == pWindow || NULL == pszText )
        {
            return;
        }
    
        pWindow->setText( MB2UTF8( pszText ) );
    }
    
  2. 给出正确的字体文件。

    1. 首先到 C:\WINDOWS\Fonts 路径下找到一个带有中文字符的字体文件 ”simhei",拷贝一下,然后放到 CEGUI SDK 的 datafiles\fonts 路径下。
    2. 复制 CEGUI SDK 的 datafiles\fonts 下的一个.font文本,然后粘贴一下(出来一个该文件的副本),更改文件名为simhei.font。然后用写字板或者记事本打开,如下文本:
      <?xml version="1.0" ?>
      <Font Name="Batang-26" Filename="batang.ttf" Type="FreeType" Size="26" NativeHorzRes="1024" NativeVertRes="768" AutoScaled="true"/>
      
      修改 Name(代表程序中使用什么名字表示该字体) 和 Filename(该字体对应的字体文件名)字段:
      <?xml version="1.0" ?>
      <Font Name="simhei" Filename="simhei.ttf" Type="FreeType" Size="26" NativeHorzRes="1024" NativeVertRes="768" AutoScaled="true"/>
      
    3. 修改对应的.scheme文件,打开TaharezLook.scheme文件,将
      <Font Filename="DejaVuSans-10.font" />
      修改为:
      <Font Filename="simhei.font" />

最后还是建议,直接修改CEGUI::String的源码,将ASCII正确地转换成 utf32 编码,这样效率更高一点,使用也更加舒服。例如,添加下列代码到CEGUI::String类中可实现直接传递宽字符:

    String( const wchar_t* utf16 )
    {
        init();
        assign( utf16 );
    }

    String& assign( const wchar_t* utf16 )
    {
        if( NULL == utf16 )
        {
            return *this;
        }

        int iNumCharacter = wcslen( utf16 );

        grow( iNumCharacter );

        utf32* pData = ptr();
        for( int i = 0; i < iNumCharacter; ++i )
        {
            pData[ i ] = utf16[ i ];
        }

        setlen( iNumCharacter );

        return *this;
    }
因为utf16绝大部分是utf32的子集,所以可以直接赋值(至少对于汉字来说可以)。

更多关于字符编码的问题,网友可以查看本人博客:彻底搞懂字符编码(unicode,mbcs,utf-8,utf-16,utf-32,big endian,little endian...)

相关文章
|
4天前
|
C语言 C++
【C++】string类(常用接口)
【C++】string类(常用接口)
13 1
|
1天前
|
编译器 C++
【C++】继续学习 string类 吧
首先不得不说的是由于历史原因,string的接口多达130多个,简直冗杂… 所以学习过程中,我们只需要选取常用的,好用的来进行使用即可(有种垃圾堆里翻美食的感觉)
7 1
|
1天前
|
算法 安全 程序员
【C++】STL学习之旅——初识STL,认识string类
现在我正式开始学习STL,这让我期待好久了,一想到不用手撕链表,手搓堆栈,心里非常爽
8 0
|
1天前
|
存储 安全 测试技术
【C++】string学习 — 手搓string类项目
C++ 的 string 类是 C++ 标准库中提供的一个用于处理字符串的类。它在 C++ 的历史中扮演了重要的角色,为字符串处理提供了更加方便、高效的方法。
6 0
|
4天前
|
C++
【C++】string类(介绍、常用接口)
【C++】string类(介绍、常用接口)
16 2
|
17天前
|
存储 网络协议 Java
Java String类
Java String类
11 0
|
21天前
|
存储 安全 C语言
【C++】string类
【C++】string类
|
22天前
|
存储 Java 编译器
Java String 类
4月更文挑战第14天
|
23天前
|
C语言 C++ Windows
标准库中的string类(下)——“C++”
标准库中的string类(下)——“C++”
|
存储 编译器 Linux
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”
标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”