近期项目中用到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的一点普及,让我茅塞顿开