现在的位置: 首页 > 综合 > 正文

浅析QStringr的Unicode存储特性

2018年06月06日 ⁄ 综合 ⁄ 共 3128字 ⁄ 字号 评论关闭

浅析QStringr的Unicode存储特性(一)

 

在看Qt相关的文档时,不断看到Qt对于i18n和Unicode的支持如何,一直没有个直观和底层的认识,最近写了些简单的例子,感觉结合实例能更好理解Qt内部对于字符串存储的转换机制。

下面的例子都以"中文"这个简单的字符串作为示例。

编译和调试工具:GCC &GDB
本地locale:zh_CN.UTF-8

字符 '中',Unicode码值U+4E2D, 对应的UTF-8编码形式为   E4 B8 AD (大端)
字符 “文”, Unicode码值U+6587,  对应的UTF-8编码形式为   E6 A6 87  (大端)

例子1 .不使用Qt,直接用char数组存储字符串

char buffer[]="中文";
cout<<buffer<<endl;

输出结果:

 
使用gdb查看buffer中存储的值

(gdb) x /8xb &buffer
0xbfa20879:     0xe4    0xb8    0xad    0xe6    0x96    0x87    0x00    0x20

可以很清楚的观察到此时buffer中存储的就是“中文”对应的UTF-8编码,由于此时输出的字符串和locale采用的是相同的编码格式,因此不会出现乱码形式。

示例2 使用QString存储字符串,并利用QChar看清楚“中文”这个字符串在QString中究竟是如何存储的。

    QString chinese="中文";
    QChar * pch=chinese.data();

上面的QString::data()的作用是返回一个指向所存储数据的QChar *指针,正如Qt的文档中所描述的一样,QString可以看作是QVector<QChar>

下面使用gdb查看pch所指向的区域存储的内容

(gdb) x /16xb pch
0x804b192:      0xe4    0x00    0xb8    0x00    0xad    0x00    0xe6    0x00
0x804b19a:      0x96    0x00    0x87    0x00    0x00    0x00    0x00    0x00

仔细观察,请注意QChar中存放的是16 bit 的Unicode的值;答案很明显,pch指向的区域存放的值依次是U+00e4,U+00b8,U+00ad,U+00e6,U+0096,U+0087,U+0000这七个Unicode码值。

是不是觉得这个序列看着有些眼熟?对了,这和第一个例子中gdb中查看到的值很相像,确切的说是值相同,不同的是示例1中的每个字节在在示例2中都用2字节来表示——这就是Qt所谓的QString的内部存储使用Unicode的底层含义!

这里,把上面的脉络稍微梳理一下:

首先,我们从示例1中已经知道,源代码中的“中文"在我的实验环境下(locale为zh_CN.UTF-8)被编码为8 bit 的 Ascii 字符串“ 0xe4    0xb8    0xad    0xe6    0x96    0x87    0x00”;

其次,在示例2中定义QString 类型的变量chinese时用"中文"为其初始化,因为QString内部存储采用Unicode,所以每个字符用16 bit来表示,因此0xe4 扩展为 0x00e4, 0xb8扩展为0x00b8,依次类推。

因此,“中文”在QString内部的存储形式就是

0xe4    0x00    0xb8    0x00    0xad    0x00    0xe6    0x00
0x96    0x00    0x87    0x00    0x00    0x00

如何,看了这个简单的例子,是否对Qt的Unicode支持还感觉很模糊呢?

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lovekatherine/archive/2007/11/04/1865639.aspx

 

浅析QStringr的Unicode存储特性(二) 

在上篇文章中,通过简单的实验弄清楚了QString究竟是如何以Unicode存储字符串的;

接下来考虑另一个问题, 假设"中文"的编码格式不同于locale,例如使用GBK编码,在上文的示例中,还会正常输出“中文"么? 答案是否定的。

考虑一下"中文"GBK编码时的整个转换过程

首先,"中文"对应的GBK编码为“ 0xd6,0xd0,0xce,0xc4,0x00“,由于QString默认情况下认为给定的字符串采用Latin-1编码,所以GBK编码被扩展为对应的16 bit Unicode码值“0x00d6,0x00d0,0x00ce,0x00c4,0x0000";再从QString到std::string的转换中,又由Unicode转换回Latin-1编码"0xd6,0xd0,0xce,0xe4,0x00",实际上又变回了最初的GBK编码,然而locale采用的是UTF-8编码,将GBK编码的字节流输出到编码格式为UTF-8的console,不出现乱码才见鬼呢。

那么,为什么上文中也存在相似的转换,却不会出现乱码呢?因为那里“中文"采用的就是locale编码格式,所以console能正确的识别并显示接受到的字节流。

那么,怎么正确显示GBK编码的"中文"呢?

首先,保证QString中存储的是"中文"对应的Unicode码值“0x4e2d 0x6587",而不是“0x00d6,0x00d0,0x00ce,0x00c4,0x0000",即完成GBK编码到Unicode的翻译。

其次,由于现在QString中的内容是"中文"的Unicode码值,而QString和std::string、const char *、QByteArray之间的转换在默认情况下是采用Latin-1编码,这样就会出现问题——0x4e2和0x6587这样的Unicode码值超出了Latin-1编码的表示范围,转换时会出现信息丢失;解决该问题的方式是显示调用 QString::setCodecForCStrings()来设定QString和const char *之间转换时采用的编码方法。

以下是代码片段
//GBK编码的"中文"
char chinese[]=......{0xd6,0xd0,0xce,0xc4,''};

QByteArray gbkbytearray(chinese);
   
QTextCodec *gbkcodec = QTextCodec::codecForName("GBK");
QTextCodec *utf8codec = QTextCodec::codecForName("UTF-8");

//设定QString与const char *之间的转换采用UTF-8编码,与locale保持一致
QTextCodec::setCodecForCStrings (utf8codec);
   
//此时 unicodestr中存放的是“中文"的Unicode码值
QString unicodestr=gbkcodec->toUnicode(gbkbytearray);

//同样的,可以借助QChar和GDB来查看QString unicodestr的内部的存储的值
QChar * pch=unicodestr.data();
   

//将QString转换为const char *并输出至console
cout<<unicodestr.toStdString().c_str()<<endl;

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lovekatherine/archive/2007/11/04/1865753.aspx

 

【上篇】
【下篇】

抱歉!评论已关闭.