近期项目中用到Qt作为编程框架,但在用到QString时发现其默认不对GBK/GB2312/GB18030提供支持,需要其中的QTextCodec类,于是仔细的研究了一下QString/QByteArray/std::string三个类的存储特点,也简单研究了一下QTextCodec的使用。仅对中文支持做了简单研究,其他语言支持暂无,不过估计跟中文支持差不多。
类介绍:
QString类:Qt提供的用于字符串处理的一个类,其组成元素是2字节的QChar,这个与16位Unicode的大小是一致的。另,Unicode分大端和小端,QString为小端,不知道是否可转为大端模式。
QByteArray类:Qt提供的类,组成元素是一个字节的char,所以它可以作为动态内存用,对于喜欢C风格内存使用的人来说这个用着很顺手,而且它不以\0为结束符,存储内容中可以包含\0,但其length函数返回仍然为实际长度而非被\0阻断之后的长度。
std::string类:c++标准的字符串类,在使用过程中感觉跟QByteArray十分相似。
QString保存数据时是以2字节的Unicode方式来保存的。
QByteArray/std::string保存数据是以单字节的方式来保存的,UTF-8的编码是没有顺序要求的,不像Unicode有大端小端区分。
默认情况(不调用QTextCodec)下,用QString的成员函数如toStdString得到std::string类型的返回值时(也就是以2字节的unicode转单字节的latin-1,内存的数据发生了很大的转变,也就是在这个过程中应该是有转码过程,从unicode到utf-8)
QString::QString(const char*)这类可以直接用单字节字符串为参数的构造函数,估计有个将字符串从latin-1到unicode的转码过程。如果这个单字节字符串是gbk编码,则转换成的unicode就成了乱码。如果原单字节字符串是gbk编码的,最好是用QTextCodec来转码,具体过程是先通过QTextCodec::codecForName("GBK")来获得转码对象(这个对象是用于在unicode和gbk之间转码的),然后用转码对象调用其成员函数QTextCodec::toUnicode()来将目标单字节字符串转为unicode编码的QString;如果要将unicode编码的QString转为单字节的QByteArray/std::string,则调用QTextCodec::fromUnicode()函数来转换。
QByteArray和std::string之间相互转换不存在太多障碍。
总之就是在使用QString的时候,一定要注意单字节字符串的编码方式,如果有必要就用QTextCodec转换一下。
QTextCodec功能:提供不同编码格式的转换,对于某一个实例来说,则是在unicode和本编码格式之间相互转换。如果要提供另外编码方式转换,例如utf-8到gbk,则需要两个实例,以unicode作为媒介(Qt是用unicode来存储字符串的,这句来自Qt的官方文档)
下面是我测试用的代码(测试环境:Ubuntu12.10 编译器:g++-4.8.5 Qt版本:5.5.0)
utf-8的测试内容
int main(int argc, char **argv) { QByteArray arrSrc = "中文的1"; QString str; QTextCodec *codecUtf8 = QTextCodec::codecForName("UTF-8"); QTextCodec *codecGbk = QTextCodec::codecForName("GBK"); str = codecUtf8->toUnicode(arrSrc); PrintQByteArrayValue(arrSrc); PrintQStringValue(str); QString qstr; QByteArray qarr; std::string stdstr; // first: ascii codes // 对于ascii编码 unicode仍然是以2字节方式存储 QString自然也这么做 也就是对于字符串"123"而言 实际存储了6个字节 // 字符串"123"的unicode存储(小端存储)方式内存形式为 31 00 32 00 33 00 qstr = "123"; qDebug("From ASCII:"); // 这里能按字节打印出字符串 "123" 在内存中的存储方式 // QString的长度length()函数返回值(这个不是字节数)反应的是字符串的字符数 这个字符数可以认为是有效字符数 // QChar是2字节 所以需要长度乘以2 PrintMemInHex((void*)qstr.data(), qstr.length() * sizeof(QChar)); // 这里只打印出了一个字符 是因为字符串 "123" 在内存中存储方式为31 00 32 00 33 00, qstrlen函数在第二个字节处发现了字符串结束符\0 然后就认为长度为1 // ps: 如果QString是以大端方式取得的值就更搞笑了 因为这里返回的长度会是0 PrintMemInHex((void*)qstr.data(), qstrlen((const char*)qstr.data())); // 这里将QString转换为std::string 能够以正常形式(ascii)打印 也就是从QString转换到std::string的过程中 有unicode向ascii编码的转换(这个需再测定) PrintMemInHex((void*)qstr.toStdString().c_str(), qstrlen((const char*)qstr.toStdString().c_str())); PrintQStringValue(qstr); stdstr = qstr.toStdString(); PrintStdStringValue(stdstr); PrintMemInHex(stdstr.c_str(), stdstr.length()); PrintMemInHex(stdstr.c_str(), qstrlen(stdstr.c_str())); qarr.clear(); qarr.append(qstr); PrintQByteArrayValue(qarr); PrintMemInHex(qarr.data(), qarr.length()); PrintMemInHex(qarr.data(), qstrlen(qarr.data())); qDebug("QString:[%s] std::string:[%s] QByteArray:[%s]\n", qstr.data(), stdstr.c_str(), qarr.data()); // second: Chinese Character // 本文件是UTF-8格式的 在本文件中给qstr赋值自然也是以UTF-8格式字符串赋值 // 字符串"中文的"的unicode存储(小端存储)方式内存形式为 2D 4E 87 65 84 76 qstr = "中文的"; qDebug("From UTF-8(Zh-CN):"); // 这里能按字节打印出字符串 "中文的" 在内存中的存储方式 长度是length函数返回值(这个不是字节数)乘以QChar大小 unicode存储方式 PrintMemInHex((void*)qstr.data(), qstr.length() * sizeof(QChar)); // 此处打印正常(unicode存储方式) PrintMemInHex((void*)qstr.data(), qstrlen((const char*)qstr.data())); // 这里将QString转换为std::string 能够以正常形式(3字节中文)打印 // 也就是从QString转换到std::string的过程中 有unicode向utf-8编码的转换(这个需再测定) PrintMemInHex((void*)qstr.toStdString().c_str(), qstrlen((const char*)qstr.toStdString().c_str())); PrintQStringValue(qstr); stdstr = qstr.toStdString(); PrintStdStringValue(stdstr); PrintMemInHex(stdstr.c_str(), stdstr.length()); PrintMemInHex(stdstr.c_str(), qstrlen(stdstr.c_str())); qarr.clear(); qarr.append(qstr); PrintQByteArrayValue(qarr); PrintMemInHex(qarr.data(), qarr.length()); PrintMemInHex(qarr.data(), qstrlen(qarr.data())); qDebug("QString:[%s] std::string:[%s] QByteArray:[%s]\n", qstr.data(), stdstr.c_str(), qarr.data()); // third: Chinese Character and ascii // 本文件是UTF-8格式的 在本文件中给qstr赋值自然也是以UTF-8格式字符串赋值 // 字符串"中文的2"的unicode存储(小端存储)方式内存数据为 2D 4E 87 65 84 76 32 00 UTF-8存储方式内存数据为E4 B8 AD E6 96 87 E7 9A 84 32 qstr = "中文的2"; qDebug("From UTF-8(Zh-CN And Ascii):"); // 这里能按字节打印出字符串 "中文的2" 在内存中的存储方式 长度是length函数返回值(这个不是字节数)乘以QChar大小 unicode存储方式 PrintMemInHex((void*)qstr.data(), qstr.length() * sizeof(QChar)); // 此处打印缺了最后一个00 与qstrlen的机制有关 可见ascii解释 PrintMemInHex((void*)qstr.data(), qstrlen((const char*)qstr.data())); // 这里将QString转换为std::string 能够以正常形式(UTF-8 + ascii)打印 // 也就是从QString转换到std::string的过程中 有unicode向UTF-8和ascii编码的转换(这个需再测定) PrintMemInHex((void*)qstr.toStdString().c_str(), qstrlen((const char*)qstr.toStdString().c_str())); PrintQStringValue(qstr); stdstr = qstr.toStdString(); PrintStdStringValue(stdstr); PrintMemInHex(stdstr.c_str(), stdstr.length()); PrintMemInHex(stdstr.c_str(), qstrlen(stdstr.c_str())); qarr.clear(); qarr.append(qstr); PrintQByteArrayValue(qarr); PrintMemInHex(qarr.data(), qarr.length()); PrintMemInHex(qarr.data(), qstrlen(qarr.data())); qDebug("QString:[%s] std::string:[%s] QByteArray:[%s]\n", qstr.data(), stdstr.c_str(), qarr.data()); // GBK test GbkTestFunc(); return 0; }
void GbkTestFunc() { qDebug("\n-------------------------"); QByteArray arrSrc = "中文的1"; QString str; QTextCodec *codecUtf8 = QTextCodec::codecForName("UTF-8"); QTextCodec *codecGbk = QTextCodec::codecForName("GBK"); str = codecGbk->toUnicode(arrSrc); PrintQByteArrayValue(arrSrc); PrintQStringValue(str); qDebug("-------------------------\n"); QString qstr; QByteArray qarr; std::string stdstr; // forth: Chinese Character // 本文件是GBK格式的 在本文件中给qstr赋值自然也是以GBK格式字符串赋值 // 字符串"中文的"的unicode存储(小端存储)方式内存形式为 2D 4E 87 65 84 76 qstr = "中文的"; qDebug("From GBK(Zh-CN):"); // 这里能按字节打印出字符串 "中文的" 在内存中的存储方式 长度是length函数返回值(这个不是字节数)乘以QChar大小 unicode存储方式 PrintMemInHex((void*)qstr.data(), qstr.length() * sizeof(QChar)); // 此处打印正常(unicode存储方式) PrintMemInHex((void*)qstr.data(), qstrlen((const char*)qstr.data())); // 这里将QString转换为std::string 能够以正常形式(3字节中文)打印 // 也就是从QString转换到std::string的过程中 有unicode向GBK编码的转换(这个需再测定) PrintMemInHex((void*)qstr.toStdString().c_str(), qstrlen((const char*)qstr.toStdString().c_str())); PrintQStringValue(qstr); stdstr = qstr.toStdString(); PrintStdStringValue(stdstr); PrintMemInHex(stdstr.c_str(), stdstr.length()); PrintMemInHex(stdstr.c_str(), qstrlen(stdstr.c_str())); qarr.clear(); qarr.append(qstr); PrintQByteArrayValue(qarr); PrintMemInHex(qarr.data(), qarr.length()); PrintMemInHex(qarr.data(), qstrlen(qarr.data())); qDebug("QString:[%s] std::string:[%s] QByteArray:[%s]\n", qstr.data(), stdstr.c_str(), qarr.data()); // fifth: Chinese Character and ascii // 本文件是GBK格式的 在本文件中给qstr赋值自然也是以GBK格式字符串赋值 // 字符串"中文的2"的unicode存储(小端存储)方式内存数据为 2D 4E 87 65 84 76 32 00 GBK存储方式内存数据为E4 B8 AD E6 96 87 E7 9A 84 32 qstr = "中文的2"; qDebug("From GBK(Zh-CN And Ascii):"); // 这里能按字节打印出字符串 "中文的2" 在内存中的存储方式 长度是length函数返回值(这个不是字节数)乘以QChar大小 unicode存储方式 PrintMemInHex((void*)qstr.data(), qstr.length() * sizeof(QChar)); // 此处打印缺了最后一个00 与qstrlen的机制有关 可见ascii解释 PrintMemInHex((void*)qstr.data(), qstrlen((const char*)qstr.data())); // 这里将QString转换为std::string 能够以正常形式(GBK + ascii)打印 // 也就是从QString转换到std::string的过程中 有unicode向GBK和ascii编码的转换(这个需再测定) PrintMemInHex((void*)qstr.toStdString().c_str(), qstrlen((const char*)qstr.toStdString().c_str())); PrintQStringValue(qstr); stdstr = qstr.toStdString(); PrintStdStringValue(stdstr); PrintMemInHex(stdstr.c_str(), stdstr.length()); PrintMemInHex(stdstr.c_str(), qstrlen(stdstr.c_str())); qarr.clear(); qarr.append(qstr); PrintQByteArrayValue(qarr); PrintMemInHex(qarr.data(), qarr.length()); PrintMemInHex(qarr.data(), qstrlen(qarr.data())); qDebug("QString:[%s] std::string:[%s] QByteArray:[%s]\n", qstr.data(), stdstr.c_str(), qarr.data()); // And Here Im gonna print Gbk as Hex directly qDebug("From GBK(Zh-CN And Ascii directly):"); PrintMemInHex("中文的2", qstrlen("中文的2")); qDebug("From GBK(Zh-CN And Ascii QByteArray):"); qarr = "中文的2"; PrintQByteArrayValue(qarr); return; }
最后参考文档:
1.Qt官方文档,全是英文的,看着比较费劲,但也提供了多数内容
2.阮一峰的关于js的字符串支持。现在项目就是在解决js的执行中编码问题,所以参考了他的一篇博文,地址是:http://www.ruanyifeng.com/blog/2014/12/unicode.html
3.一个同事关于iso-8859-1的一点普及,让我茅塞顿开