字符串乱码解决之道

January 5th, 2017 by JasonLe's Tech Leave a reply »

最近工作压力太大,blog也逐渐荒废,怎么也得写点东西了,要不说不过去。。。。。

Qt为字节流和字符串分别提供了QByteArray和QString两个类(还有QLatin1String等其他类,但这两个是最主要的)。当我们涉及到I/O时,比如读写文件、读写网络socket、控制台输入输出、读写串口… 操作的都是字节流,如果我们此时需要操作的内容是字符串,则需要二者之间的相互转换。在C和C++中,我们一般都是将 “Hello World!” 这种称为字符串。但是就目前而言,当我们提字符串时,一般是指一个Unicode字符串,其由一个一个的Unicode字符构成;当我们提字节流时,是指一个一个的字节。或许我们可以说,ANSI C/C++截止目前只有字节流,而缺乏对字符串的支持。另外各个编译器对编码的支持又严重不一, Qt为解决这个问题提供了QTextCodec。

QTextCodec * textc = QTextCodec::codecForName("GBK");
1.QTextCodec::setCodecForCStrings(textc);
2.QTextCodec::setCodecForTr(textc); 
3.QTextCodec::setCodecForLocale(textc);

QString 是不存在中文支持问题的,很多人遇到问题,并不是本身 QString 的问题,而是没有将自己希望的字符串正确赋给QString。很简单的问题,”我是中文”这样写的时候,它是传统的char 类型的窄字符串,我们需要的只不过是通过某种方式告诉QString 这四个汉字采用的那种编码。而问题一般都出在很多用户对自己当前的编码没太多概念。另外文件是有编码的,但是这种纯文本文件却不会记录自己采用的编码,这个是问题的根源。真的是 QString 乱码了吗?其实很简单的一个问题,当你从窄字符串 char* 转成Unicode的QString字符串的时候,你需要告诉QString你的这串char* 中究竟用的是什么编码?GBK、BIG5还是Latin-1。理想情况就是:将char* 传给QString时,同时告诉QString自己的编码是什么;但是QString 提供的成员函数,远远满足不了大家的需求,于是只有采取语句1的办法。

tr(“中文”);与QString(“中文”);一样,你必须告诉tr这个窄字符串是何种编码?你不告诉它,它就用latin1。于是所谓的乱码问题就出来了。如何告诉tr你写的这几个汉字在磁盘中保存的是何种编码呢?这正是语句2所做的。如果你的编码采用的utf8,可以直接使用trUtf8而不必设置setCodecForTr()。

对于语句3应该没什么好说的,在绝大多数情况下,我们在代码中应该都用不到这个函数(默认的system应该比我们所能设置的要好)。
下面讲一下关于编码转换问题:
QT中的QString内容使用Unicode作为文本编码。但是实际系统中通常采用的是其他编码,例如GBK,utf8等。为了便于兼容这些格式,QT中还设置了两个字符串类型:
QCString类: C类型字符串,必须以\0结尾,也就是中间不能含有\0. 例如GBK编码的字符串
QByteArray类: 中间可以含有\0.例如utf8编码的字符串

在设置下面的代码基础上:

QTextCodec *gbk = QTextCodec::codecForName("gb18030"); 
QTextCodec *utg8 = QTextCodec::codecForName("utf-8");
QTextCodec::setCodecForTr(gbk);
QTextCodec::setCodecForLocale(gbk);
QTextCodec::setCodecForCStrings(gbk);

1. UTF-8 转换 GBK

QString U2G(QString utfStr)
{
   return gbk->toUnicode(utfStr.toLocal8Bit());
}

2 GBK 转换 UTF-8

QString U2G(QString gbkStr)
{
   return utg8->toUnicode(gbkStr.toUtf8());
}

代码示例:
———————————————————————————–

 QTextCodec *gbk = QTextCodec::codecForName("gb18030");
 QTextCodec *utf8 = QTextCodec::codecForName("utf-8");

 QTextCodec::setCodecForTr(gbk);
 QTextCodec::setCodecForLocale(gbk);
 QTextCodec::setCodecForCStrings(gbk);


 QFile file("../test.txt");
 file.open(QIODevice::ReadOnly);
 QByteArray readByte = file.readAll();
 QString readStr = utf8->toUnicode(readByte.data());
 file.close();
 QString utfStr = QObject::trUtf8(readByte); //utf-8
 QString gbkStr = QObject::tr("中文"); // gbk

 QString utf2gbk = gbk->toUnicode(readStr.toLocal8Bit()); // utf8 conver gbk
 QString gbk2utf1 = utf8->toUnicode(utf2gbk.toUtf8()); // gbk convert utf8
 QString g2u = gbk->toUnicode(gbk->fromUnicode(readStr)); // gbk convert utf8

 qDebug() << "gbk:" << gbkStr;
 qDebug() << "utf8:" << utfStr;
 qDebug() << "readStr:" << readStr;

 qDebug() << "read_size:" << readByte.length();
 qDebug() << "utf2gbk:" <<utf2gbk << "length:" << readStr.toLocal8Bit().length();
 qDebug() << "gbk2utf8-1:" << gbk2utf1 << " length: " << utf2gbk.toUtf8().length();
 qDebug() << "g2u" << g2u << "length:" << gbk->fromUnicode(utfStr).length();

 QLabel *label = new QLabel(utf2gbk);
 label->show();

参考:

【1】http://www.cnblogs.com/bingcaihuang/archive/2011/03/17/1986714.html

【2】http://knight4576.blog.51cto.com/2761974/703963