Qt——保存文件、打印、调试传值正常,但myMessageOutput()打印乱码,需警惕“QTextCodec::setCodecForLocale(codec);”

15 篇文章 2 订阅

注意:若转载,请贴上链接“https://blog.csdn.net/qq_41042595/article/details/134944637”,如若发现抄袭或未标明来源现象,都可举报反馈!!!


前言

在实现日志管理的功能并调试确定可用后,把数据以“CSV”文件格式进行保存 的功能也进行实现,没想到,那些代码影响到了日志管理的中文内容在 “应用程序输出”输出台 的显示,变乱码了??!实际上就是那 两行或者一行 代码导致的,不是什么大问题,但是!!!花了我时间呀,,,还好最后挣扎时,请教了大神,才能这么快解决问题,所以,在此记录下来,以供大家参考。
日志管理代码与解决方案2)的代码结合就会出现该问题

编码不规范,亲人两行泪
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,选择后右边显示了该函数的用法、相关代码:
日志管理的代码Qt提供了
不急,完整代码我将在最后面拷贝出来,因为这只是一个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)
只有ANSI编码时中文正常显示,而UTF8的乱码

编码问题【1】有两个解决方案

QTextStream1默认保存数据以ANSI编码(即扩展的ASCII编码2);但要求项目统一使用UTF-8编码3,解决方案如下,使得保存的中文内容正常显示,且不影响打印的中文内容显示:
解决后的文档是UTF8编码,并且文本内容可以正常显示中文了

1)用QTextStream类setCodec()函数【推荐使用】

    QTextStream out(&file);                                                                                 // 默认保存为ANSI编码
    out.setCodec("UTF-8");                                                                                  // 可能遇到自动插入UTF-8 BOM的,则需要setGenerateByteOrderMark(false)
    out.setGenerateByteOrderMark(false);                                                                    // 如果为false,则不插入BOM
    out << strTest << "\n";

解决方案1)的解释_1
解决方案1)的解释_2

参考文献:
QT读写文件方法介绍以及编码问题解决
字符编码、QString编码、Qt界面乱码问题总结

2)用QTextCodec类的setCodecForLocale()函数【不推荐使用】

//    QTextCodec * codec = QTextCodec::codecForName("UTF-8");                                                 // 作用于QTextStream,其默认数据保存为ANSI,而该两行使之自动保存为UTF-8
//    QTextCodec::setCodecForLocale(codec);
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));                                       // 相当于上面两行代码

解决方案2)的解释
从图中看到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());

参考文献:
Qt中文乱码问题的关键:QString按UTF-8编码理解常量字符串
Qt字符编码

问题解决了,调试器显示QByteArray中文依然乱码,但不影响结果

调试器里,对于QByteArray的中文读取,可能使用另外一种编码,所以主要看打印的结果怎样来判断中文是否转换正确,而不是一直盯着 调试器的转换 过程:
中文乱码_1
中文乱码_2

延伸:不同编译器等环境对局部变量的自动赋值
    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不用找,就能让中文正常打印、写入文件中的!!!
QtCreator文本编辑器的编码

参考文献:
彻底解决Qt中文乱码以及汉字编码的问题(UTF-8/GBK)【该文章提供了很多种 解决编码 的问题,但不一定都适合,所以要多试,需对症下药,方可解决】
QT中如何识别编译器和实现条件编译以及设定目标文件生成路径

总结

  1. QTextStream推荐使用(这样不影响 打印的中文显示、也不影响 保存中文数据)
        QTextStream out(&file);                                                                                 // 默认保存为ANSI编码
        out.setCodec("UTF-8");                                                                                  // 可能遇到自动插入UTF-8 BOM的,则需要setGenerateByteOrderMark(false)
        out.setGenerateByteOrderMark(false);                                                                    // 如果为false,则不插入BOM
        out << strTest << "\n";
    
  2. 局部变量要人为赋个初值
  3. 涉及到中文数据写入数据库情况时,在pro文件中添加如下代码
# QMAKE_CXXFLAGS解释:额外的编译选项 的方式增加额外的编译选项
# win32-msvc*解释:仅msvc编译器;win32-g++解释:仅mingw编译器;linux-g++*解释:仅g++编译器
win32-msvc*{
QMAKE_CXXFLAGS += /utf-8
}
win32-g++{
}

  1. QTextStream 类(文本流)和 QDataStream 类(数据流)Qt文件编码转换【博客作者自制、开源的代码】 ↩︎

  2. 刨根究底字符编码之七——ANSI编码与代码页ANSI是什么编码? ↩︎

  3. 一篇搞懂Unicode ANSI UTF8等字符编码 ↩︎

  • 25
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yvette_QIU

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值