注意:若转载,请贴上链接“https://blog.csdn.net/qq_41042595/article/details/134944637”,如若发现抄袭或未标明来源现象,都可举报反馈!!!
目录
前言
在实现日志管理的功能并调试确定可用后,把数据以“CSV”文件格式进行保存 的功能也进行实现,没想到,那些代码影响到了日志管理的中文内容在 “应用程序输出”输出台 的显示,变乱码了??!实际上就是那 两行或者一行 代码导致的,不是什么大问题,但是!!!花了我时间呀,,,还好最后挣扎时,请教了大神,才能这么快解决问题,所以,在此记录下来,以供大家参考。
PS:此为微信合成表情,表情包来源是“微信 Achi 作者《小熊虫过兔年》系列”。
环境
-
开发环境
- Windows11 操作系统
- Qt 5.15.0 C++GUI框架
-
Desktop Qt 5.15.0 MinGW 64-bit
编译器
-
Desktop Qt 5.15.0 MSVC2019 64-bit
编译器
- qmake 构建系统
- Qt Creator 11.0.3 编辑器
- UTF-8 编码字符集
PS:该项目代码在两个编译器下都能正常运行!
日志管理 简单实现(QtCreator-帮助)
打开QtCreator——点击“帮助”——在“索引”的“查找”中输入qInstallMessageHandler,选择后右边显示了该函数的用法、相关代码:
不急,完整代码我将在最后面拷贝出来,因为这只是一个Demo,重要的是解决过程,以防下次再次难住自己;
数据保存至CSV文件中
用QDir、QFile判断指定路径及对应文件是否存在,不存在则自动生成;然后用QTextStream把数据放到文本流中,写入文件中,然后文件关闭,如下main.cpp代码(pro文件无更改):
#include <QFile>
#include <QDir>
#include <QTextStream>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString dirPath = "G:/QtTest/untitled/infolist";
QDir dir(dirPath);
if (!dir.exists()) {
bool ok = dir.mkpath(dirPath); // 创建多级目录
}
QString fullInfoFilePath = dirPath + "/info.csv";
QFile file(fullInfoFilePath);
if (!file.exists()) {
qDebug() << "new file...";
}
file.open( QIODevice::ReadWrite | QIODevice::Append | QIODevice::Text );
QString strTest = "46848,白白,1579789;";
qDebug() << strTest;
QTextStream out(&file); // 默认保存为ANSI编码
out << strTest << "\n";
file.close();
return a.exec();
}
但是结果,不统一:
——打印(UTF-8)
new file...
"46848,白白,1579789;"
——文件内容(ANSI)
编码问题【1】有两个解决方案
QTextStream
1默认保存数据以ANSI编码(即扩展的ASCII编码2);但要求项目统一使用UTF-8编码3,解决方案如下,使得保存的中文内容正常显示,且不影响打印的中文内容显示:
1)用QTextStream类setCodec()函数【推荐使用】
QTextStream out(&file); // 默认保存为ANSI编码
out.setCodec("UTF-8"); // 可能遇到自动插入UTF-8 BOM的,则需要setGenerateByteOrderMark(false)
out.setGenerateByteOrderMark(false); // 如果为false,则不插入BOM
out << strTest << "\n";
2)用QTextCodec类的setCodecForLocale()函数【不推荐使用】
// QTextCodec * codec = QTextCodec::codecForName("UTF-8"); // 作用于QTextStream,其默认数据保存为ANSI,而该两行使之自动保存为UTF-8
// QTextCodec::setCodecForLocale(codec);
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); // 相当于上面两行代码
从图中看到setCodecForLocale()
的 警告,意思就是,该代码在哪里开始执行,后续都自动设置对应编码了,即,一运行就是全局设置统一编码!
但是真的好用吗?
——从我这个提问就能看出,这个函数没有它表述的那样真的顺利,请继续往下看,但是时间紧急的话,请直接用1)用QTextStream类setCodec()函数【推荐使用】解决方案;
假设使用“2)”解决方案
编码问题【2】出现
问题代码
创建了一个QWidget项目,如图所示的步骤操作:
main.cpp代码如下:
#include "widget.h"
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <QApplication>
#include <QString>
#include <QDebug>
#include <QTextCodec>
#include <QFile>
#include <QDir>
// 有没有static都可以用
static void myMessageOutput(QtMsgType type, const QMessageLogContext & context, const QString & msg)
{
printf("----: %s\n", qPrintable(msg)); // 打印方式 1
QByteArray localMsg = msg.toLocal8Bit();
// QTextCodec * codec = QTextCodec::codecForLocale();
// QByteArray localMsg = codec->fromUnicode(msg);
const char *file = context.file ? context.file : "";
const char *function = context.function ? context.function : "";
printf("Debug: %s\n", localMsg.constData()); // 打印方式 2
switch (type) {
case QtDebugMsg:
fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
case QtInfoMsg:
fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
}
}
void printString(const QString & str) {
QTextCodec* codec = QTextCodec::codecForLocale(); // 获取终端或调试器所使用的字符编码
QByteArray encodedMsg = codec->fromUnicode(str); // 转换为终端或调试器所使用的字符编码
printf("%s\n", encodedMsg.constData()); // 输出转换后的字符串,打印方式 3
}
int main(int argc, char *argv[])
{
// qInstallMessageHandler(&myMessageOutput); // 放在QApplication上下都可以
QApplication a(argc, argv);
qInstallMessageHandler(&myMessageOutput);
int x, y;
qDebug() << QString("%1, %2").arg(x).arg(y); // 打印:MSVC——Debug:“28, 197735392”或“0, -902168576”或。。。,Release:“-1284964320, -1284964320”或“1080164384, 1080164384”或。。。;MinGW——Debug:“0, 50887728”或“0, 50428976”或。。。,Release:“0, 0”
x = 0, y =0;
qDebug() << QString("%1, %2").arg(x).arg(y); // 打印出“0, 0”
// QTextCodec * codec = QTextCodec::codecForName("UTF-8"); // 作用于QTextStream,默认数据保存为ANSI,而该两行使之自动保存为UTF-8
// QTextCodec::setCodecForLocale(codec);
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); // 相当于上面两行代码
QString dirPath = "G:/QtTest/untitled/infolist";
QDir dir(dirPath);
if (!dir.exists()) {
bool ok = dir.mkpath(dirPath); // 创建多级目录
}
QString fullInfoFilePath = dirPath + "/info.csv";
QFile file(fullInfoFilePath);
if (!file.exists()) {
qDebug() << "new file...";
}
file.open( QIODevice::ReadWrite | QIODevice::Append | QIODevice::Text );
QString strTest = "46848,白白,1579789;";
qDebug() << strTest;
QTextStream out(&file); // 默认保存为ANSI编码
out << strTest << "\n";
file.close();
const char *str = "你好";
QString test = QString("你好");
printf("--- %s\n", qPrintable(test)); // -----(Y)
// printf("--- %s\n", test.toUtf8().data()); // -----(N)
qDebug() << str << test ;
QString chinese = QString::fromUtf8("你好,世界!");
printString(chinese); // -----(Y)
Widget w;
w.show();
return a.exec();
}
运行结果:
分析过程
我以为是传入的值有问题,或者,在myMessageOutput()函数中,QString转成QByteArray的方式使得值有问题!
而在调试时,会发现,const QString & msg
的值是没问题的,而QByteArray localMsg = msg.toLocal8Bit();
之后localMsg变成乱码了——难道是QString转成QByteArray的方式使得值有问题?,如下图所示(其实这样判断中文是否转换正确的方式,是不合理的,后面将进行补充):
所以试了很多种QString转换QByteArray的方式,以及尝试将fprintf()改用printf()了,但都像在做无用功似的!
后面大神指导,注释掉这行代码后,问题迎刃而解,这才找到问题关键处:
QTextCodec * codec = QTextCodec::codecForName("UTF-8"); // 作用于QTextStream,默认数据保存为ANSI,而该两行使之自动保存为UTF-8
QTextCodec::setCodecForLocale(codec);
// QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); // 相当于上面两行代码
解决后完整代码——使用“1)”解决方案
不用“2)”解决方案,那么“数据保存至CSV文件中”的编码问题依然存在,所以改用“1)”解决方案,不就两个编码问题都解决了?!!!如下图所示:
main.cpp代码如下:
#include "widget.h"
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <QApplication>
#include <QString>
#include <QDebug>
#include <QTextCodec>
#include <QFile>
#include <QDir>
static void myMessageOutput(QtMsgType type, const QMessageLogContext & context, const QString & msg)
{
printf("----: %s\n", qPrintable(msg)); // 打印方式 1
QByteArray localMsg = msg.toLocal8Bit();
// QTextCodec * codec = QTextCodec::codecForLocale();
// QByteArray localMsg = codec->fromUnicode(msg);
const char *file = context.file ? context.file : "";
const char *function = context.function ? context.function : "";
printf("Debug: %s\n", localMsg.constData()); // 打印方式 2
switch (type) {
case QtDebugMsg:
fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
case QtInfoMsg:
fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
case QtWarningMsg:
fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
case QtCriticalMsg:
fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
case QtFatalMsg:
fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
break;
}
}
void printString(const QString & str) {
QTextCodec* codec = QTextCodec::codecForLocale(); // 获取终端或调试器所使用的字符编码
QByteArray encodedMsg = codec->fromUnicode(str); // 转换为终端或调试器所使用的字符编码
printf("%s\n", encodedMsg.constData()); // 输出转换后的字符串,打印方式 3
}
int main(int argc, char *argv[])
{
// qInstallMessageHandler(&myMessageOutput); // 放在QApplication上下都可以
QApplication a(argc, argv);
qInstallMessageHandler(&myMessageOutput);
int x, y;
qDebug() << QString("%1, %2").arg(x).arg(y); // 打印:MSVC——Debug:“28, 197735392”或“0, -902168576”或。。。,Release:“-1284964320, -1284964320”或“1080164384, 1080164384”或。。。;MinGW——Debug:“0, 50887728”或“0, 50428976”或。。。,Release:“0, 0”
x = 0, y =0;
qDebug() << QString("%1, %2").arg(x).arg(y); // 打印出“0, 0”
QString dirPath = "G:/QtTest/untitled/infolist";
QDir dir(dirPath);
if (!dir.exists()) {
bool ok = dir.mkpath(dirPath); // 创建多级目录
}
QString fullInfoFilePath = dirPath + "/info.csv";
QFile file(fullInfoFilePath);
if (!file.exists()) {
qDebug() << "new file...";
}
file.open( QIODevice::ReadWrite | QIODevice::Append | QIODevice::Text );
QString strTest = "46848,白白,1579789;";
qDebug() << strTest;
QTextStream out(&file); // 默认保存为ANSI编码
out.setCodec("UTF-8"); // 可能遇到自动插入UTF-8 BOM的,则需要setGenerateByteOrderMark(false)
out.setGenerateByteOrderMark(false); // 如果为false,则不插入BOM
out << strTest << "\n";
file.close();
const char *str = "你好";
QString test = QString("你好");
printf("--- %s\n", qPrintable(test)); // -----(Y)
// printf("--- %s\n", test.toUtf8().data()); // -----(N)
qDebug() << str << test ;
QString chinese = QString::fromUtf8("你好,世界!");
printString(chinese); // -----(Y)
Widget w;
w.show();
return a.exec();
}
只要不用“2)”解决方案,中文正常打印的方式则多种多样
// ------1
const char *str = "你好";
printf("--- %s\n", qPrintable(str));
// ------2
QString chinese = QString::fromUtf8("你好,世界!");
QTextCodec* codec = QTextCodec::codecForLocale();
QByteArray encodedMsg = codec->fromUnicode(str);
printf("%s\n", encodedMsg.constData());
// ------3
QString msg = QString("你好");
QByteArray localMsg = msg.toLocal8Bit();
printf("Debug: %s\n", localMsg.constData());
问题解决了,调试器显示QByteArray中文依然乱码,但不影响结果
调试器里,对于QByteArray的中文读取,可能使用另外一种编码,所以主要看打印的结果怎样来判断中文是否转换正确,而不是一直盯着 调试器的转换 过程:
延伸:不同编译器等环境对局部变量的自动赋值
int x, y;
qDebug() << QString("%1, %2").arg(x).arg(y); // 打印:MSVC——Debug:“28, 197735392”或“0, -902168576”或。。。,Release:“-1284964320, -1284964320”或“1080164384, 1080164384”或。。。;MinGW——Debug:“0, 50887728”或“0, 50428976”或。。。,Release:“0, 0”
x = 0, y =0;
qDebug() << QString("%1, %2").arg(x).arg(y); // 打印出“0, 0”
在MinGW时,只有Release下,几个int类型的局部变量,就几个都自动初始化、赋值为0;
而Debug下,只有第一个int类型局部变量会自动初始化为0,其他不管了;
在MSVC时,Debug、Release下,都是乱赋值,无规律的!
尽量养成规范的编码习惯,局部变量的定义,最好人为赋初值。
参考文献:
Qt debug模式和release模式的区别
其他编码问题:将中文字段名或内容等数据写入SQLite数据库中,出现“语法编译不通过 C2xx”的问题
临时方案:
在中文最后面加个字母——中文可以不乱码,但是加个“数字、空格”就会乱码;
不想中文后面加字母,那么中文只能赋两个字的值,非2个字都会报错!
需要根治该问题,请看如下的“解决方案”!!!
解决方案
MinGW编译器与QtCreator文本编辑器的编码一致,大部分为UTF-8编码;
而MSVC编译器与电脑系统的编码一致,大部分为GBK编码;
所以,只有识别到MSVC编译器时才要求使用UTF-8,这样编译时,自动寻找相关文件了;
在pro文件中添加如下代码:
# QMAKE_CXXFLAGS解释:额外的编译选项 的方式增加额外的编译选项
# win32-msvc*解释:仅msvc编译器;win32-g++解释:仅mingw编译器;linux-g++*解释:仅g++编译器
win32-msvc*{
QMAKE_CXXFLAGS += /utf-8
}
win32-g++{
}
要是无win32-msvc*
属性的指定/区分,MinGW编译器也会识别该代码QMAKE_CXXFLAGS += /utf-8,这样反而会产生问题——当编译时MinGW找不到相关文件,出现 中文数据乱码 问题!!!实际上MinGW不用找,就能让中文正常打印、写入文件中的!!!
参考文献:
彻底解决Qt中文乱码以及汉字编码的问题(UTF-8/GBK)【该文章提供了很多种 解决编码 的问题,但不一定都适合,所以要多试,需对症下药,方可解决】
QT中如何识别编译器和实现条件编译以及设定目标文件生成路径
总结
- QTextStream推荐使用(这样不影响 打印的中文显示、也不影响 保存中文数据)
QTextStream out(&file); // 默认保存为ANSI编码 out.setCodec("UTF-8"); // 可能遇到自动插入UTF-8 BOM的,则需要setGenerateByteOrderMark(false) out.setGenerateByteOrderMark(false); // 如果为false,则不插入BOM out << strTest << "\n";
- 局部变量要人为赋个初值
- 涉及到中文数据写入数据库情况时,在pro文件中添加如下代码
# QMAKE_CXXFLAGS解释:额外的编译选项 的方式增加额外的编译选项
# win32-msvc*解释:仅msvc编译器;win32-g++解释:仅mingw编译器;linux-g++*解释:仅g++编译器
win32-msvc*{
QMAKE_CXXFLAGS += /utf-8
}
win32-g++{
}