由中文乱码引来的一系列学习——Qt

前言

解决中文引起的乱码,并不难,网上一搜就有好几个方法任君选择,但是解决乱码的这些方法的原理是什么,我一直没太明白。

这次项目需要在Android环境下运行,而根据Qt跨平台的特性,我一般是在Windows环境下(MSVC2019_32bit)先进行测试:Windows环境下是是使用预编译(# pragma execution_character_set("utf-8"))解决乱码的,而我在Android下并没有使用任何措施防止中文乱码,但是Android下并没有出现中文乱码的情况,这个是为什么呢?带着这个疑问,我开始在网上各种学习此类知识,在此记录下。

Android没有出现中文不乱码的原因

在Android中,默认的字符编码通常是UTF-8,这是因为Android的API和框架大部分都是使用Java编写的,而Java字符串和字符编码默认使用UTF-8。

Windows中文乱码的原因和解决方案

Windows中文乱码的原因

整体来说,编译器msvc默认源文件的中文为gbk(ANSI),但是QString处理时会按utf-8解析,这样就会用utf-8解析gbk的,导致编码错误。

详细分析如下:

一般情况下,都是使用QString对字符进行存储,进而在控件中展示。比如

ui->label->setText("中文标签");

等同于

    const QString& text = QString("中文标签");
    ui->label->setText(text);

QString类存储的是Unicode字符,帮助文档对QString的构造函数也说了对于传入的常量字符串指针会通过 fromUtf8() 方法转换为Unicode。

QString::QString(const char *str)

Constructs a string initialized with the 8-bit string str. The given const char pointer is converted to Unicode using the fromUtf8() function.

使用 fromUtf8() 进行转换,也说明了Qt 默认传入的是 utf-8 格式的编码,但此处真的是 utf-8 格式的编码吗?并不是,MSVC默认是ANSI编码,最直观的是可以看下构建生成的.obj文件(cl.exe(微软的C编译器)来编译源代码,并生成.obj文件),可以看出编译生成的文件编码格式是ANSI,可以找到"中文标签",并未乱码。

所以由于"中文标签" 在此环境下是ANSI编码,而Qt却默认utf-8格式的,使用错误的方法将其转换为Unicode。

Windows解决中文乱码的方案

这就得出解决乱码的方案,可以从几个不同方向进行解决:

使用正确的编码方法进行转换

  • 使用QString::fromLocal8Bit (告诉Qt用本地方式转换)

      const QString& text = QString::fromLocal8Bit("中文标签");
      ui->label->setText(text);
    

查看QString::fromLocal8Bit的源代码是使用QTextCodec实现的,所以也可以自己直接调用QTextCodec。

QString QString::fromLocal8Bit_helper(const char *str, int size)
{
    if (!str)
        return QString();
    if (size == 0 || (!*str && size < 0)) {
        QStringDataPtr empty = { Data::allocate(0) };
        return QString(empty);
    }
#if QT_CONFIG(textcodec)
    if (size < 0)
        size = qstrlen(str);
    QTextCodec *codec = QTextCodec::codecForLocale();
    if (codec)
        return codec->toUnicode(str, size);
#endif // textcodec
    return fromLatin1(str, size);
}
  • 使用QTextCodec

      QTextCodec *codec = QTextCodec::codecForLocale();
      ui->label->setText(codec->toUnicode("中文标签"));
    

设置编译器的编码方式为utf-8

  • 使用杂记(#pragma execution_character_set( "utf-8" ) )标记此处编码

指定用于字符串和字符文本的执行字符集。 用 u8 前缀标记的文本不需要此指令。

#pragma execution_character_set( "utf-8" )

execution_character_set pragma | Microsoft Learn

但是官方文档对此用法不推荐:

此编译器指令在 Visual Studio 2015 Update 2 及更高版本中已过时。 建议将 /execution-charset:utf-8/utf-8 编译器选项与包含扩展字符的窄字符和字符串文本的 u8 前缀一起使用。

  • 设置C/C++命令行选项 /utf-8

将源字符集和执行字符集指定为 UTF-8。

更多说明见/utf-8 (将源和执行字符集设置为 UTF-8) | Microsoft Learn

通过此设置,可以在看一下编译生成的obj 文件,可以看到在utf-8编码格式下查看,"中文标签"并没有乱码。

使用前缀标记文本为Unicode

  • 直接使用前缀u8

    ui->label->setText(u8"中文标签");

c++11 增加了对Unicode的支持:

C++11引入以下两种新的内置数据类型来存储不同编码长度的Unicode数据:

  • char16_t: 用于存储UTF-16编码的Unicode数据。
  • char32_t: 用于存储UTF-32编码的Unicode数据。

至于UTF-8编码的Unicode数据,C++11还是使用8字节宽度的char类型的数组来保存。而char16_t和char32_t的长度则犹如其名称所显示的那样,长度分别为16字节和32字节,对任何编译器或者系统都是一样的。此外,C++11还定义了一些常量字符串的前缀。在声明常量字符串的时候,这些前缀声明可以让编译器使字符串按照前缀类型产生数据。事实上,C++11一共定义了3种这样的前缀:

u8表示UTF-8编码

u表示为UTF-16编码

U表示为UTF-32编码

  • 使用Qt的宏 QStringLiteral(使用前缀u)

    ui->label->setText(QStringLiteral("中文标签"));

QStringLiteral是一个从字符串常量创建QString对象的宏。

宏在编译时,从字符串文字生成QString数据,QString的内部数据将在编译时生成,在运行时不会发生任何转换或内存分配,使用QStringLiteral来代替C++中的双重数值传递将会在编译的时候显著的提升运行效率。

QStringLiteral的源码如下,从中看出使用了前缀u 标记字符串常量。

#define QT_UNICODE_LITERAL(str) u"" str
#define QStringLiteral(str) \
    ([]() noexcept -> QString { \
        enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \
        static const QStaticStringData<Size> qstring_literal = { \
            Q_STATIC_STRING_DATA_HEADER_INITIALIZER(Size), \
            QT_UNICODE_LITERAL(str) }; \
        QStringDataPtr holder = { qstring_literal.data_ptr() }; \
        return QString(holder); \
    }()) \

关于源字符集和执行字符集的说明

  • 源字符集: 源文件字符集是指源文件保存时按源字符集编码方式编码后的字符集。VS2019源字符集编码格式默认是GBK格式,在利用VS编写代码时,源代码会自动编码为GBK字符集。
  • 执行字符集: 程序运行时所使用的字符集,编译器会将源字符集先按照源字符集编码方式进行解码,再将解码后得到的字符按照执行字符集编码方式编码为执行字符集。执行字符集一般默认使用Windows系统本地字符编码,若是简体中文系统,则是GBK或GB2312字符集。

Unicode in Qt

在Qt官方文章

Unicode in Qt | Qt 5.15

中说:

Unicode is the standard for encoding text in almost all languages spoken in the world. It is nowadays used as the native encoding for text on most modern operating systems. The major exception is Microsoft Windows that still has a dual system supporting code pages and Unicode for applications.

翻译过来是:

Unicode是用于编码世界上几乎所有语言的文本的标准。如今,它已成为大多数现代操作系统的默认编码方式。唯一的例外是微软Windows系统,它对于应用程序仍然有双系统去支持代码页和Unicode。

这句话,也显出Windows系统与其他操作系统的 区别,也是存在中文乱码的原因。

Qt对Unicode提供的支持:

In Qt, and in most applications that use Qt, most or all user-visible strings are stored using Unicode. Qt provides:

  • Translation to/from legacy encodings for file I/O: see QTextCodec and QTextStream.
  • Support for locale specific Input Methods and keyboards.
  • A string class, QString, that stores Unicode characters, with support for migrating from C strings including fast translation to and from UTF-8, ISO8859-1 and US-ASCII, and all the usual string operations.
  • Unicode-aware UI controls.
  • Unicode compliant text segmentation (QTextBoundaryFinder)
  • Unicode compliant line breaking and text rendering
相关推荐
囚生CY6 分钟前
【学习笔记】蒙特卡洛与强化学习
笔记·python·学习
心之所想,行之将至25 分钟前
零基础开始学习鸿蒙开发-交友软件页面设计
学习·交友
biter00882 小时前
opencv(15) OpenCV背景减除器(Background Subtractors)学习
人工智能·opencv·学习
Code哈哈笑3 小时前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
QQ同步助手4 小时前
如何正确使用人工智能:开启智慧学习与创新之旅
人工智能·学习·百度
流浪的小新4 小时前
【AI】人工智能、LLM学习资源汇总
人工智能·学习
A懿轩A5 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
南宫生12 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
sanguine__13 小时前
Web APIs学习 (操作DOM BOM)
学习
数据的世界0115 小时前
.NET开发人员学习书籍推荐
学习·.net