当前位置: 首页 > java >正文

QT开发---字符编码与QString和QByteArray

字符编码的发展史

字符编码是计算机存储和传输文本的基础,其发展伴随计算机技术的全球化而不断演进

早期编码(单字节编码)

  • ASCII 码(1963 年):美国信息交换标准代码,用 7 位二进制表示 128 个字符(含英文字母、数字、标点和控制字符),是最早的通用编码。
  • 扩展 ASCII:部分厂商将 8 位二进制用于编码,扩展出 128 个额外字符(如欧洲语言的特殊符号),但缺乏统一标准,导致兼容性问题。

多字节编码(区域化编码)

为解决非英语字符的表示问题,各国推出本地化编码:

  • GB2312(1980 年):中国国家标准,收录 6763 个简体汉字,采用双字节编码,兼容 ASCII。
  • GBK(1995 年):扩展 GB2312,收录 21003 个汉字,包含繁体和日韩汉字。
  • Big5(1984 年):中国台湾地区的繁体汉字编码,收录 13053 个汉字。
  • Shift_JIS、EUC-JP:日本针对日语(含假名、汉字)的编码。
  • ISO-8859 系列:国际标准化组织制定的多语言编码,如 ISO-8859-1 覆盖西欧语言,ISO-8859-5 支持西里尔字母。

统一编码(全球化编码)

  • Unicode(1991 年):由 Unicode 联盟推出,目标是为世界上所有字符分配唯一编码,涵盖文字、符号、表情等,初期用 16 位表示(支持 65536 个字符),后扩展至 32 位,理论可容纳 100 多万个字符。
  • UTF-8(1992 年):Unicode 的可变长度编码方案,用 1-4 字节表示字符:英文字母占 1 字节(兼容 ASCII),汉字通常占 3 字节,解决了 Unicode 存储和传输效率问题,成为互联网和跨平台应用的主流编码。
  • UTF-16/UTF-32:Unicode 的其他实现方式,UTF-16 用 2 或 4 字节,UTF-32 固定 4 字节,适用于特定场景(如 Windows 内核、Java 内部)。

随着移动互联网和全球化的推进,字符编码逐渐向 UTF-8 统一,主流系统(Windows、macOS、Linux)和编程语言(Python 3、Java、JavaScript)均默认支持 UTF-8,有效解决了多语言文本的兼容问题。同时,Unicode 不断更新,纳入新字符(如 emoji 表情)以适应时代需求。

QString

QString是 Qt 框架中用于处理字符串的核心类,专为 Unicode 设计,提供了丰富的字符串操作功能,是 Qt 开发中最常用的数据类型之一。

QString 存储由 16 位 QChar 组成的字符串,每个 QChar 对应一个 UTF-16 编码单元。(代码值超过 65535 的 Unicode 字符通过代理对存储,即两个连续的 QChar)

Unicode 是一项国际标准,支持当今使用的大多数书写系统。它是 US-ASCII(ANSI X3.4-1986)和 Latin-1(ISO 8859-1)的超集,且所有 US-ASCII/Latin-1 字符在相同的代码位置上均可使用

在底层实现中,QString 采用隐式共享(写时复制)机制,以减少内存占用并避免不必要的数据复制。这也有助于降低存储 16 位字符(而非 8 位字符)带来的固有开销

除了 QString,Qt 还提供 QByteArray 类,用于存储原始字节和传统的以 '\0' 结尾的 8 位字符串。在大多数情况下,QString 是首选类。它在整个 Qt API 中被广泛使用,而 Unicode 支持确保了应用程序便于本地化翻译(若你希望在未来拓展应用市场)。QByteArray 适用的两个典型场景是:需要存储原始二进制数据时,以及内存占用至关重要的场景(如嵌入式系统) 

基本特性

  • Unicode 支持:内部以 UTF-16 编码存储字符,可无缝处理全球各种语言的字符,包括汉字、日文、韩文等复杂文字
  • 动态内存管理:自动管理内存分配与释放,无需手动处理字符串长度和内存空间
  • 隐式共享:采用写时复制(Copy-On-Write)机制,高效处理字符串的复制和传递,减少内存占用和开销
  • 空字符串处理:区分空字符串("")和 null 字符串,API 设计友好

常见操作

这里只对QString和QByteArray类中常用方法介绍,若要深入学习,可以在QT文档中查看更多祥光内容,也可在网上查阅相关资料。

字符串创建与初始化

//字符串创建与初始化// 直接初始化QString str1 = "Hello, Qt!";QString str2("你好,Qt!");// Qt风格格式化QString str3 = QString("数值:%1, 字符串:%2").arg(100).arg("test");qDebug() << str1;qDebug() << str2;qDebug() << str3;

字符串拼接 

//字符串拼接//重载运算符+str1 = "Q";str1 = str1 + "string";qDebug() << str1;//append在字符串尾部添加str1.append("QT");//prepend在字符串头部添加str1.prepend("TQ");qDebug() << str1;

计算字符串长度

//length() size() 字符串的字符数量
//count()无参时,统计字符数量
//count()有参时,统计字符出现次数
int n = str.count("l"); 
qDebug() << str.length() << str.size() << str.cout();
//计算字节大小
int bytes = str.toUtf8().size(); // 返回 UTF-8 编码的字节数

字符串判断是否为空

bool isEmpty() 字符串是否为空字符串(即长度为 0)
bool isNull() 字符串是否为空值(null),即从未被初始化或显式设置为 null
//QString 是值类型,默认构造的字符串是空字符串 "",不是 null
qDebug() << str.isEmpty() << str.isNull();

判断是否包含子字符串

contains()
bool QString::contains(const QString &str,Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
bool QString::contains(QLatin1String str,Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
bool QString::contains(QChar ch,Qt::CaseSensitivity cs = Qt::CaseSensitive) const;
enum CaseSensitivity {CaseInsensitive = 0,  // 不区分大小写CaseSensitive   = 1   // 区分大小写(默认)
};
//基本子串查找(最常用)
bool result = str.contains("QT"); 默认区分大小写
//查字符
bool result = contains(QChar('x'));

提取子串 

QString::section() 是 Qt 中用于 按分隔符把字符串切成若干段并取出其中一段 的快捷函数。
它不会改变原串,而是返回一个新的 QString
QString QString::section(QChar sep,int start,int end = -1,QString::SectionFlags flags = SectionDefault) const;
str = "/abc/def/gh";
str = section('/',2,2);
str = section('/',2,3);

0x00  对空字段进行计数,不包括前导和尾随分隔符,并且区分大小写对分隔符进行比较

0x01  将空字段视为不存在      

0x02   把前导分隔符包含在结果中

0x04   把尾随分隔符包含在结果中

0x08   匹配分隔符时忽略大小写

字符串分割 

  • QStringLiteral 是 Qt 提供的一个宏,用于在编译期将字符串字面量直接转换成 QString 的内部数据结构(QStringData)。

    • 优势:避免了运行时的内存分配和字符编码转换(如从 const char* 到 UTF-16),提升了性能,尤其在频繁使用相同字符串时。

    • 适用场景:常用于创建短小的、硬编码的字符串(如 "OK"),或需要高效构造 QString 的地方。

QString::split() 用于把单个 QString 按指定的“分隔符”切成若干段,返回 QStringList
QStringList QString::split(const QString &sep,Qt::SplitBehavior behavior = Qt::KeepEmptyParts,Qt::CaseSensitivity cs     = Qt::CaseSensitive) const;QStringList QString::split(QChar sep,Qt::SplitBehavior behavior = Qt::KeepEmptyParts,Qt::CaseSensitivity cs     = Qt::CaseSensitive) const;QStringList QString::split(const QRegularExpression &re,Qt::SplitBehavior behavior = Qt::KeepEmptyParts) const;
behavior:
Qt::KeepEmptyParts(默认)保留空片段
Qt::SkipEmptyParts 丢弃空片段QString str1 = "a,,b,c";
QStringList list2 = str1.split(',', Qt::SkipEmptyParts);

字符串移除 

QString 的 remove() 系列接口用来原地删除字符、子串或符合正则的片段;
返回的是 QString&,支持链式调用
QString& remove(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive);
QString& remove(const QString& str, Qt::CaseSensitivity cs = Qt::CaseSensitive);
QString& remove(const QRegularExpression& re);
QString& remove(int position, int n);   // 从 position 开始删 n 个字符str1.remove(',');           // 把逗号全部删掉
str1.remove(",,");          // 删除连续两个逗号
str2.remove(1, 2);          // 从下标 1 开始删 2 个字符
str3.remove(QRegularExpression("\\d+"));  // 删除所有数字串remove() 会直接修改原 QString字符串

字符串转换 

数值 → 字符串
QString::number(value, format, precision);
参数
value      任何整数或浮点类型
format     字符:
‑ 'f'  定点格式(3.140000)
‑ 'e'  科学计数法(3.14e+00)
‑ 'g'  自动选用 f 或 e(默认值)
‑ 'G'  同 g,但用 E 代替 e
precision  小数位数/有效数字,默认 6字符串 → 数值
int    i = str.toInt   (str);     
float  f = str.toFloat (str);    
double d = str.toDouble(str);
默认按 10 进制,可用第二个参数指定进制 2~36

与其他字符串类的对比

字符串类型特点适用场景
QString支持 Unicode,功能丰富,Qt 原生支持Qt 应用程序内部字符串处理
std::string标准 C++ 类,默认不支持 Unicode跨平台非 Qt 项目,标准库兼容场景
const char*C 风格字符串,手动管理内存与 C 语言库交互,底层操作

注意事项

  • 编码转换与外部系统交互时(如文件 IO、网络传输),需明确编码格式(如 UTF-8、GBK),使用toUtf8()fromLocal8Bit()等方法进行转换
  • 性能考量:频繁的字符串拼接操作建议使用QStringBuilder(通过%运算符)提高效率
  • 隐式共享在多线程环境中操作字符串时,需注意写时复制机制可能带来的线程安全问题

QByteArray

QByteArray是 Qt 框架中用于处理字节数据的核心类,专为存储原始二进制数据和 8 位字符串设计,提供了高效的字节操作功能。

    QByteArray 可用于存储原始字节(包括 '\0')和传统的以 '\0' 结尾的 8 位字符串。使用 QByteArray 比使用 const char * 更为便捷。在底层实现中,它始终确保数据后紧跟一个 '\0' 终止符,并采用隐式共享(写时复制)机制,以减少内存占用并避免不必要的数据复制

    除了 QByteArray,Qt 还提供 QString 类用于存储字符串数据。在大多数情况下,QString 是首选类。它将内容视为 Unicode 文本(采用 UTF-16 编码),而 QByteArray 则尽量避免对所存储字节的编码或语义做任何假设(除少数使用 ASCII 编码的遗留场景外)。此外,QString 在整个 Qt API 中被广泛使用。QByteArray 适用的两个主要场景是:需要存储原始二进制数据时,以及内存占用至关重要的场景(例如,在嵌入式 Linux 上使用 Qt 时)

    基本特性

    • 双重用途:既可存储原始字节(包括\0字符),也可存储以\0结尾的传统 8 位字符串
    • 自动管理:内部维护\0终止符,无需手动处理字符串结束标记
    • 隐式共享:采用写时复制(Copy-On-Write)机制,优化内存使用和数据复制效率
    • 兼容性:与 C 风格字符串(const char*)无缝交互,简化传统 C API 调用

    常见操作

    //创建与初始化    // 直接初始化QByteArray ba1 = "Hello";QByteArray ba2("World", 5); // 指定长度// 从原始数据创建char Data[] = {0x00, 0x01, 0x02, 0x03};QByteArray ba3(Data, 4);// 填充初始化QByteArray ba4(10, 'A'); // 创建10个'A'的字节数组qDebug() << ba1 << ba2 << ba3 << ba4;

    QByteArray中许多方法与QString相似,这里不再重复说明,只介绍QByteArray中的方法

    QByteArray::data()返回指向QByteArray数据的可写指针(char*),
    用于直接操作底层字节数据
    QByteArray::constData()返回指向QByteArray数据的只读指针(const char*),
    常用于将QByteArray传递给需要 C 风格字符串(const char*)的函数
    QByteArray::fill(char c, int size = -1)用指定字符c填充QByteArray
    如果不指定size,则填充整个QByteArray;如果指定size,则填充到指定长度
    QByteArray byteArray(5, 0); //创建长度为5,初始值为0的字节数组
    byteArray.fill('X'); // 用 'X' 填充数组
    // 十六进制转换
    toHex() 用于将QByteArray中的数据转换为十六进制表示的QByteArray
    fromHex() 则是将十六进制表示的QByteArray或QString转换回原始字节数据
    QByteArray hexBa = ba.toHex();       
    QByteArray fromHex = QByteArray::fromHex(hexBa); // Base64编码
    toBase64() 用于将QByteArray中的数据转换为 Base64 编码的QByteArray
    fromBase64() 则是将 Base64 编码的QByteArray或QString转换回原始字节数据
    QByteArray base64Ba = ba.toBase64(); 
    QByteArray fromBase64 = QByteArray::fromBase64(base64Ba);

    与 QString 的对比

    特性QByteArrayQString
    内部编码8 位字节(无特定编码)UTF-16(Unicode)
    主要用途原始二进制数据、8 位字符串文本字符串、多语言支持
    内存占用更节省(每个字符 1 字节)较高(每个字符 2 字节)
    适用场景文件 IO、网络传输、二进制数据界面文本、多语言处理
    编码感知无(需手动处理编码)有(原生支持 Unicode)

    注意事项

    • 当需要处理文本数据时,优先使用QString并明确编码转换
    • 使用constData()而非data()获取只读数据,避免不必要的复制
    • 对大型字节数组进行频繁修改时,可使用reserve()预分配空间提升性能
    • 与外部系统交互时,需明确数据编码格式(如 UTF-8、Latin-1)

    stringQByteArray之间转换

    std::string → QByteArray
    使用静态方法 QByteArray::fromStdString(),直接将标准字符串转换为字节数组
    std::string stdStr = "Hello World";
    QByteArray ba = QByteArray::fromStdString(stdStr); 
    // 内部按 std::string 的原始字节(通常是系统默认编码)存储QByteArray → std::string
    使用 QByteArray::toStdString() 方法,将字节数组转换为标准字符串
    QByteArray ba = "Hello World";
    std::string stdStr = ba.toStdString();

    std::string 与 QString 互转(需经 QByteArray 中转) 

    QStringQByteArray之间转换

    QString 转 QByteArray

    QString内部以 UTF-16 编码存储,转换为QByteArray时需指定目标编码格式

    转为 UTF-8 编码(推荐)
    QString str = "Hello World";
    QByteArray ba = str.toUtf8(); 转为本地编码
    QByteArray ba = str.toLocal8Bit();转为 Latin-1 编码
    QByteArray ba = str.toLatin1(); 转为 UTF-16 编码
    保留QString内部编码(UTF-16),可指定字节序(大端 / 小端)
    //带字节序标记(BOM)
    QByteArray ba = str.toUtf16();
    //不带BOM,指定小端字节序
    QByteArray ba = str.toUtf16(QTextCodec::LittleEndian);

    QByteArray 转 QString

    QByteArray存储的字节数据需明确编码格式才能正确转为QString(Unicode)
    从 UTF-8 编码转换(推荐)
    QByteArray ba = "Hello World";
    QString str = QString::fromUtf8(ba);从本地编码转换
    QString str = QString::fromLocal8Bit(ba);从 Latin-1 编码转换
    QString str = QString::fromLatin1(ba);从 UTF-16 编码转换
    需指定字节序(与转换时一致)
    // 从带BOM的UTF-16字节转换
    QString str = QString::fromUtf16(reinterpret_cast<const ushort*>(ba.constData()));
    // 从指定小端字节序的UTF-16字节转换(无BOM)
    QString str = QString::fromUtf16(reinterpret_cast<const ushort*>(ba.constData()), ba.size()/2, QTextCodec::LittleEndian);

    关键注意事项

    • 编码一致性:转换双方必须使用相同的编码格式,否则会出现乱码。

    • 特殊字符处理

      • QByteArray包含无法被目标编码识别的字节,QString会用(替换字符)表示。
      • 非 ASCII 字符(如中文、emoji)在toLatin1()转换时可能丢失信息(被替换为?)。
    • 空数据处理

      • QString"")转换后为包含\0QByteArray
      • 包含\0QByteArray转换为QString时,\0会被视为正常字符(而非字符串结束符)。

    结语:

    无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力

     

    http://www.xdnf.cn/news/16167.html

    相关文章:

  • 深度分析Java内存回收机制
  • 基于深度学习的图像分类:使用EfficientNet实现高效分类
  • RocketMQ搭建及测试(Windows环境)
  • 大模型处理私有数据的核心技术
  • 【News】同为科技亮相首届气象经济博览会
  • Django Models详解:数据库模型的核心
  • 第二十七章 W55MH32 Interrupt示例
  • go语言基础教程:【1】基础语法:变量
  • 爬虫基础概念
  • 数学基础弱能学好大数据技术吗?
  • Kubernetes 集群架构和Pod创建流程
  • tcp基础协议
  • 字节的机器人模型 GR-3
  • 高可用架构模式——如何应对接口级的故障
  • uni-app支付宝小程序样式穿透失效
  • C51:用DS18B20传感器读取温度
  • 如何将拥有的域名自定义链接到我的世界服务器(Minecraft服务器)
  • 【Rust线程】Rust高并发编程之线程原理解析与应用实战
  • 【unity游戏开发入门到精通——组件篇】unity的粒子系统力场 (Particle System Force Field)实现如旋风、吸引力、风吹效果等
  • 数据库垂直拆分和水平拆分
  • 【​I2S:芯片设计中的“音频桥梁”​】
  • Android Service 全解析:从基础原理到实战优化
  • Windows11 本地安装docker Desktop 部署dify 拉取镜像报错
  • 【DataWhale】快乐学习大模型 | 202507,Task06笔记
  • 游戏装备被盗,运营商赔不赔
  • Petalinux的常用指令
  • 【Linux | 网络】应用层(HTTPS)
  • Python 程序设计讲义(7):Python 的基本数据类型——整数类型
  • Linux 或者 Ubuntu 离线安装 ollama
  • Paimon的部分更新以及DeleteVector实现