现代Qt开发教程(新手篇)1.3——字符串与编码

张开发
2026/4/12 8:54:39 15 分钟阅读

分享文章

现代Qt开发教程(新手篇)1.3——字符串与编码
现代Qt开发教程新手篇1.3——字符串与编码相关仓库仍然已经开源正在积极火热的建设之中欢迎各位大佬提Issue和PR链接地址https://github.com/Awesome-Embedded-Learning-Studio/Tutorial_AwesomeQt1. 前言C 字符串的坑我踩过太多了说实话字符串处理这事儿在 C 里就是个雷区。std::string只知道是一堆字节但啥编码没人知道。std::wstringWindows 是 UTF-16Linux 是 UTF-32跨平台直接裂开。更别提各种 GBK、UTF-8 互相转换转来转去最后全是问号。我第一次做 Qt 项目的时候从网络收到的 JSON 里的中文全是乱码折腾了一整夜最后发现是没搞清楚 QString 是 UTF-16 存储的而我拿 UTF-8 的 QByteArray 直接去构造。从那以后我就发誓一定要把 Qt 的字符串体系搞明白。Qt 其实提供了一个非常完整的字符串处理体系搞懂了它编码问题基本就不存在了。这一篇我们就不讲虚的直接把 QString、QByteArray、QStringView 这三兄弟的关系捋清楚再加上编码转换的那些坑包教包会。环境说明本文代码基于 Qt 6.5 版本。Qt 6 中字符串类的行为与 Qt 5 基本一致但建议使用新式 API如QStringView替代QStringRef。示例代码在 C17 或更高版本下编译通过。2. QString 是什么和 std::string 有啥本质区别先说结论QString 内部存储的是 UTF-16 编码的 Unicode 字符而 std::string 只是原始字节序列不知道自己是什么编码。这意味着什么std::string 存一个中文字符 “你”UTF-8 编码下需要 3 个字节但 std::string 只知道自己是 3 个字节不知道这是一个字符。而 QString 知道自己是 1 个 QChar准确说是 1 个 Unicode 码位可以用 size() 得到 1用 at(0) 拿到这个字符。这个区别看着简单但实际影响非常大// std::string 的困惑std::string s你好;// UTF-8 编码实际 6 字节size_t lens.length();// 得到 6而不是字符数 2// QString 的清晰QString qsQString::fromUtf8(你好);size_t lenqs.length();// 得到 2真正的字符数QString 内部用 UTF-16 存储这意味着每个字符至少占 2 字节。对于常用汉字一个 QChar 就够了。但对于超出基本多文种平面的字符比如 emoji可能需要两个 QChar代理对不过这是进阶话题入门阶段我们先记住QString 能正确处理 Unicode 字符。随堂测验口述回答用自己的话说说为什么 std::string 的 length() 不能准确返回中文字符数而 QString 可以(请先自己想一下再往下滑看答案)━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━答案参考std::string 是字节序列不知道编码length() 返回的是字节数UTF-8 中一个汉字占 3 字节所以 length() 返回 6 而不是 2QString 内部用 UTF-16每个 QChar 代表一个 Unicode 码位length() 返回字符数━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━3. QByteArray 是什么什么时候用QByteArray 也很简单它就是一个能够自动管理内存的字节数组。你可以把它看作带内存管理的 char*。那什么时候用 QByteArray什么时候用 QString记住一句话如果是纯文本、需要显示给人看的用 QString如果是二进制数据、网络传输、文件原始内容用 QByteArray。// 文本内容 → QStringQString username张三;// 网络接收的原始数据 → QByteArrayQByteArray rawDatasocket.readAll();// 文件二进制内容 → QByteArrayQByteArray fileDatafile.readAll();QByteArray 还有一个常见用途和 C 库交互。很多 C API 接受 const char*QByteArray 可以直接提供QByteArray dataHello;C_Function(data.constData());// 自动转 const char*4. QStringView —— 零拷贝的字符串视图Qt 5.12 引入了 QStringViewQt 6 里大量使用。它的核心思想是零拷贝的字符串只读视图。什么叫零拷贝当你有一个 QString想调用一个函数来处理它但又不想复制整个字符串用 QStringView 就行voidprocessString(QStringView sv);// 接受任何字符串类型的视图QString sHello;processString(s);// 从 QString 构造视图零拷贝processString(uHello);// 从字符数组构造视图零拷贝QStringView 本身不拥有数据只是指向别的数据。这意味着它非常轻量复制一个 QStringView 只是复制两个指针数据指针和长度不复制实际内容。Qt 6 里很多 API 都改成了接受 QStringView比如 QString 的很多成员函数// Qt 5 风格会有拷贝boolcontains(constQStringstr)const;// Qt 6 风格零拷贝boolcontains(QStringView str)const;不过入门阶段你不需要过度优化直接用 QString 传引用也没问题。QStringView 主要在你需要处理大量字符串操作、又想避免频繁分配内存时有用。⚠️坑 #1QStringView 的生命周期陷阱❌ 错误做法返回一个指向临时对象的 QStringViewQStringViewgetView(){returnQString(Hello);// 临时对象被销毁视图悬空} ✅ 正确做法只返回值类型或者确保被观察对象生命周期足够长 cpp QString getView() { return QString(Hello); // 返回值安全 } 后果悬空引用可能导致崩溃或读取垃圾数据 一句话记住QStringView 不拥有数据要确保被观察对象活得比视图久5. 编码转换 —— GBK、UTF-8 互相转换的坑编码转换是国内 Qt 开发者的必经之路。Windows 默认编码可能是 GBKLinux/Mac 通常是 UTF-8网络传输通常用 UTF-8这之间来回转换确实容易出错。5.1 QString 和 UTF-8 互转Qt 6 里最简单的方式// UTF-8 → QStringQByteArray utf8Data...;// UTF-8 编码的字节QString strQString::fromUtf8(utf8Data);// QString → UTF-8QByteArray utf8Datastr.toUtf8();这是最常见的用法也是推荐用法。现代系统基本都统一到 UTF-8 了能不用别的编码就不用。5.2 GBK 转换Windows 旧代码兼容Windows 下有些老旧接口或文件会用 GBK 编码// GBK → QStringQt 6 推荐方式QByteArray gbkData...;// GBK 编码的字节QStringDecoderdecoder(GBK);// Qt 6 新方式QString strdecoder(gbkData);// QString → GBKQStringEncoderencoder(GBK);QByteArray gbkDataencoder(str);注意如果系统没有对应编码的转换器decoder/encoder 会处于无效状态转换会得到空字符串。可以检查 isValid()QStringDecoderdecoder(GBK);if(!decoder.isValid()){qWarning()GBK encoding not supported;}随堂测验调试挑战以下代码有什么问题在 Windows 中文环境下会输出什么QFilefile(data.txt);// 假设文件是 GBK 编码if(file.open(QIODevice::ReadOnly)){QByteArray datafile.readAll();QString textQString::fromUtf8(data);// 直接按 UTF-8 解析qDebug()text;} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ **答案参考** - 文件是 GBK 编码但代码用 fromUtf8 解析会导致乱码 - 中文字符会变成问号或乱码 - 正确做法是检测文件编码或使用 QStringDecoder(GBK) 解析 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ### 5.3 QStringLiteral —— 编译期字符串字面量 如果你在代码里写固定的字符串字面量用 QStringLiteral 宏 cpp // 运行时构造有开销 QString str QString(Hello); // 编译期生成零运行时开销 QString str QStringLiteral(Hello);QStringLiteral 会在编译期就把字符串转成 UTF-16 格式运行时直接使用避免运行时转换。不过这只对字面量有用不能用于变量QString sWorld;QString strQStringLiteral(s);// 错误QStringLiteral 只接受字面量6. QString 常用操作速查字符串操作是日常开发的高频操作这里列几个最常用的QString strHello,Qt,World;// 分割QStringList partsstr.split(,);// [Hello, Qt, World]// 查找boolhasQtstr.contains(Qt);// trueintposstr.indexOf(Qt);// 6// 替换str.replace(Qt,C);// Hello,C,World// 格式化类似 sprintfQString msgQString(用户 %1 登录成功代码 %2).arg(张三).arg(200);// 用户 张三 登录成功代码 200// 数字转换QString numStrQString::number(3.14,f,2);// 3.14doublevaluenumStr.toDouble();// 3.14// 去空格QString trimmed hello .trimmed();// hello⚠️坑 #2QString::number 的精度陷阱❌ 错误做法QString::number(3.1415926535) 默认只有 6 位精度✅ 正确做法QString::number(3.1415926535, ‘f’, 10) 指定精度 后果浮点数精度丢失计算结果可能不正确 一句话记住数字转字符串记得指定精度格式不然默认 6 位不够用7. 字符串拼接的性能问题Qt 5 时代大家都被教导不要用 号拼接字符串因为会有多次内存分配。Qt 6 里虽然优化了很多但了解一下还是有好处。// Qt 5/6 都可以但可能有多次分配QString sHello;s ;sWorld;s!;// 推荐方式一次构建QString sQString(Hello%1World%2).arg( ).arg(!);// 或者用 QStringList joinQStringList parts;partsHelloWorld!;QString sparts.join( );不过说实话对于日常使用 号拼接的性能影响通常可以忽略除非你在循环里拼接大量字符串。先写正确再考虑优化。8. 练习项目练习项目日志文件编码转换工具功能描述写一个命令行工具能够读取文本文件并转换编码。例如将 GBK 编码的日志文件转换为 UTF-8 编码。✅完成标准程序接受两个命令行参数输入文件路径和输出文件路径能够检测或指定输入文件编码至少支持 GBK 和 UTF-8转换后输出为 UTF-8 编码转换失败时给出清晰的错误提示能够处理大文件逐行读取不一次性加载全部内容提示用 QFile QTextStream 逐行读取QTextStream 可以设置编码stream.setEncoding(QStringConverter::Utf8)用 QStringDecoder/QStringEncoder 处理编码转换错误处理检查文件是否能打开、编码转换是否有效9. 官方文档参考 Qt 6 QString 类文档 · QString 完整 API 参考 Qt 6 QByteArray 类文档 · 字节数组处理二进制数据必备 Qt 6 QStringView 类文档 · 零拷贝字符串视图 Qt 6 字符串处理概述 · 字符串体系全景图 Qt 6 QStringEncoder/QStringDecoder 文档 · 现代 Qt 6 编码转换方式链接已验证2026-03-17 可访问到这里你就掌握了 Qt 字符串处理的基础。编码转换这事儿说难不难但一定要搞清楚每个环节的编码是什么不然就是满天飞乱码。下一篇我们来讲 Qt 的容器类和字符串配合使用非常频繁。━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━相关阅读入门 · 环境搭建 · 00 · Qt6 安装踩坑指南 - 相似度 100%现代Qt开发——0.1——如何在IDE中配置Qt环境 - 相似度 100%现代Qt教程——0.2——第一个 CMake Qt6 工程从零跑通 - 相似度 80%

更多文章