本文基于:Qt Creator 4.11.1 Based on Qt 5.14.1 (MSVC 2017, 32 bit)
1、写入文件编码格式乱码的问题
QString saveFileName = QFileDialog::getSaveFileName(
this,
"保存文件",
QCoreApplication::applicationDirPath(),
"文本文件 (*.txt)");
if(saveFileName.isEmpty()) return;
QFile qFile(saveFileName);
if(!qFile.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
QMessageBox::warning(this, "错误", "文件保存失败!");
return;
}
QTextStream saveStream(&qFile);
saveStream.setGenerateByteOrderMark(true); // 写入BOM头
saveStream.setCodec("UTF-8"); // 设置UTF-8编码
saveStream << "123你好"; //出现乱码
qFile.close();
QMessageBox::information(this, "成功", "文件保存成功!");
上面这种直接将字符串"123你好"传递给saveStream,即使设置编码格式和写入BOM头都会出现中文乱码。这个问题还是比较复杂的先来了解几个概念。
源码(mainwindow.cpp)存储编码方式:就是.cpp以哪一种编码方式存储在本地,最常见的就是UTF-8,如上面代码的"123你好"在编译前(也就还是.cpp文件)是以UTF-8存储在计算机里面,以十六进制表示为:31 32 33 e4 bd a0 e5 a5 bd (这是计算机识别的,通过UTF-8解释成 "123你好" 就是我们人看的了)。
编译器解释编码方式:编译器解释也就是可以看作把源文件编译成可执行文件。因为编译器打开文件后看到的也全是一堆二进制,所以同样需要一种编码格式来完成解释工作(这里有一系列工作如把所有字符串放入字符串数据区域),同样常见的编码方式有UTF-8。
运行时环境对编译器解释后的文件处理编码方式:运行编译器解释后的文件,运行环境也有自己的编码方式去处理字符串数据。
总结:所有一个文件从源文件到运行共有三个编码方式,而且还是分离的。说到这里顺便说一下为什么英文和数字就不会出现乱码呢?这是因为标准 ASCII 码对照表的0~127对应关系在任何编码方式(UTF-8、GBK等等)都是一样的,所以无论你的编码方式是如何配置英文数字都不会乱码,除非你自己定义个编码方式!!!!
回到原来问题
为什么saveStream << "123你好"; 会乱码成:123ä½ å¥½,其十六进制为 31 32 33 c3 a4 c2 bd c2 a0 c3 a5 c2 a5 c2 bd 。使用十六进制查看文件可以使用notepad++,其工具栏最右边有一个H图标。这个十六进制明显和UTF-8下的"123你好"的十六进制31 32 33 e4 bd a0 e5 a5 bd,明显不一样,所以是写入时就发生了编码错误,而不是读取文件的时候发生了错误。
既然是写入时发生错误,可以检查UTF-8编码存储方式下的C字符串在Qt运行时在内存中是不是31 32 33 e4 bd a0 e5 a5 bd,可以运行如下代码:
const char* utf8Str = u8"你好";
QByteArray bytes(utf8Str);
qDebug() << "十六进制是:" << bytes.toHex();
结果发现在内存中也是 31 32 33 e4 bd a0 e5 a5 bd,因此也就是数据从内存到文件的写入过程中被错误转码,那么也就是说应该是QTextStream或QFile未正确设置UTF-8输出导致的,而不是编译器和运行环境的问题。再进一步排查为了进一步确定是QTextStream还是QFile的问题。不用QTextStream直接用QFile对象进行写入实验,参考代码如下:
QString saveFileName = QFileDialog::getSaveFileName(
this,
"保存文件",
QCoreApplication::applicationDirPath(),
"文本文件 (*.txt)");
if(saveFileName.isEmpty()) return;
QFile qFile(saveFileName);
if(!qFile.open(QIODevice::WriteOnly | QIODevice::Truncate )) {
QMessageBox::warning(this, "错误", "文件保存失败!");
return;
}
qFile.write("123你好");
qFile.close();
结果是没有出现乱码!!!因此就是QTextStream的问题了,原因可能是QTextStream将const char*视为本地编码(如GBK)。
解决方案:
1、就是想使用QTextStream和QFile的组合写入文件,首先saveStream.setCodec("UTF-8"); 指定编码方式,第二个是const char*不要行中文,有的话先转为QString再输入到QTextStream对象中,这样也不会乱码。也就是如下两行代码:
saveStream.setCodec("UTF-8");
saveStream << QString("123你好");
2、第二种方式直接使用QFile对象进行文件写入,避免QTextStream的编码。
qFile.write("你好");
2、QStandardItemModel中当读取到item是空项出现程序崩溃
场景如下,目的就是保存下面QStandardItemModel对象的数据:

QString saveFileName = QFileDialog::getSaveFileName(
this,
"保存文件",
QCoreApplication::applicationDirPath(),
"文本文件 (*.txt)");
if(saveFileName.isEmpty()) return;
QFile qFile(saveFileName);
if(!qFile.open(QIODevice::WriteOnly | QIODevice::Truncate )) {
QMessageBox::warning(this, "错误", "文件保存失败!");
return;
}
QTextStream saveStream(&qFile);
saveStream.setGenerateByteOrderMark(true); // 写入BOM头
saveStream.setCodec("UTF-8"); // 设置UTF-8编码
QString str = ""; //用来做为数据缓冲
//获取表头数据
for (int i=0; i<columnCount; ++i) {
QStandardItem* item = model->horizontalHeaderItem(i);
str += item->text() + "\t";
}
str += "\n";
//获取每一行数据
for(int i=0; i<model->rowCount(); ++i){
for(int j=0; j<columnCount-1 ; ++j){
QStandardItem* item = model->item(i, j);
str += item->text() + "\t";
}
//婚姻
if(model->item(i, columnCount-1)->checkState() == Qt::Checked){
str += QString("是") + "\t";
}else{
str += QString("否") + "\t";
}
str += "\n";
}
saveStream << str;
qFile.close();
QMessageBox::information(this, "成功", "文件保存成功!");
对于没有TableView中没有空项的情况下,上面代码用来保存数据是没有问题的。但是一旦出现空项上面代码的运行就会导致程序崩溃,这是因为空item。比如下面我们创建一行,并把一个item数据删除掉,然后点击保存,并使用调试就会看到报出的错误.

进行保存,出现下面报错

表示出现分隔错误,这是因为上面保存代码没有对item有效性进行检查,当item是空项是它就是一个空指针,这时候调用它的Text()方法就会导致程序崩溃。解决方法可以在每次使用item前增加如下代码对其有效性进行检查:
if(!item){
continue; //检查是不是空项,是空项就跳过
}
&spm=1001.2101.3001.11974&articleId=147523404&d=1&t=3&u=9d89ad5c56764addb0c653f929478900)
2148


&spm=1001.2101.3001.11976&articleId=147523404&d=1&t=3&u=f926964d73344b1f9741898fa67944c5)

被折叠的 条评论
为什么被折叠?



