【C++指南】string(四):编码

【C++指南】string(四):编码
           💓 博客主页:倔强的石头的ZEEKLOG主页 

           📝Gitee主页:
倔强的石头的gitee主页

            ⏩ 文章专栏:《C++指南》

                                  期待您的关注



引言

在 C++ 编程中,处理字符串是一项极为常见的任务。而理解字符串在底层是如何编码存储的,对于编写高效、健壮且可移植的代码至关重要。



本文将深入探讨 C++ 中string所涉及的多种编码规则,包括 ASCII、Unicode、UTF - 8、UTF - 16 和 UTF - 32 等,并着重讲解 UTF - 8 编码以及它在string中灵活存储字符串的机制。

常见编码规则介绍

ASCII 编码

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是最古老且最基础的编码方式之一。它使用 7 位二进制数来表示 128 个字符,包括英文字母(大小写)、数字、标点符号以及一些控制字符。例如,大写字母 'A' 的 ASCII 码是 65,用二进制表示为01000001。ASCII 编码的优点是简单直观,易于理解和处理,在早期的计算机系统中被广泛应用。然而,它的局限性也很明显,只能表示英文字符和少量特殊符号,无法满足全球多语言文本处理的需求。

参考文章:

【C语言指南】ASCII码完整详细介绍_c语言ascll码-ZEEKLOG博客

Unicode 编码

Unicode 旨在为世界上所有的字符提供一个唯一的数字编号,无论其语言和平台如何。

它涵盖了几乎所有已知的字符集,包括各种语言的字母、汉字、符号、表情符号等。

Unicode 有多种编码形式,常见的有 UTF - 8、UTF - 16 和 UTF - 32。

Unicode 的编码空间非常大,使用 16 位或更多位来表示字符。例如,汉字 “你” 的 Unicode 编码是U+4F60。

Unicode 解决了全球字符统一编码的问题,但由于其编码长度不固定(对于一些基本拉丁字符也使用 16 位表示),在存储和传输时可能会造成空间浪费。

UTF - 8 编码

UTF - 8(8 - bit Unicode Transformation Format)是一种变长编码方式,它可以使用 1 到 4 个字节来表示一个字符。

UTF - 8 的设计目标是既能与 ASCII 编码兼容(对于 ASCII 字符,UTF - 8 编码与 ASCII 编码完全相同,占用 1 个字节),又能高效地表示其他 Unicode 字符。

对于大多数常用字符(如英文字母、数字、标点符号等),UTF - 8 仅用 1 个字节表示,与 ASCII 编码一致,这使得基于 ASCII 的现有软件可以无缝处理 UTF - 8 编码的文本。

对于其他非 ASCII 字符,UTF - 8 会根据字符的 Unicode 值使用 2 到 4 个字节进行编码。

例如,汉字 “你” 的 UTF - 8 编码是E4 BD A0,占用 3 个字节。

UTF - 8 编码具有良好的空间效率和兼容性,在互联网上被广泛应用,是目前最常用的编码方式之一。

UTF - 16 编码

UTF - 16 也是一种 Unicode 编码形式,它使用 16 位(2 个字节)或 32 位(4 个字节)来表示一个字符。

对于基本多文种平面(BMP)内的字符,UTF - 16 使用 16 位表示;

而对于那些在 BMP 之外的字符(如一些生僻字、表情符号等),则需要使用两个 16 位的代码单元来表示,即占用 4 个字节。

UTF - 16 在 Windows 操作系统以及一些基于 Unicode 的编程语言(如 Java)中应用较为广泛,但由于其对于非 BMP 字符的处理相对复杂,在跨平台和网络传输方面不如 UTF - 8 灵活。

UTF - 32 编码

UTF - 32 是一种定长编码方式,每个字符都固定占用 4 个字节(32 位)。

它直接将 Unicode 码点映射到 32 位的二进制数,这种编码方式简单直接,字符定位和处理非常方便,因为每个字符的长度固定。

然而,其缺点也很明显,对于大量只包含基本拉丁字符的文本,会造成大量的空间浪费。

例如,一个简单的英文字符串 “hello”,在 UTF - 32 编码下需要占用 20 个字节(每个字符 4 个字节),而在 UTF - 8 编码下只需要 5 个字节。

以下将对UTF-8这一较为广泛使用的编码规则展开详细讲解:

UTF - 8 编码详解

UTF - 8 编码规则

UTF - 8 的编码规则如下:

  1. 单字节字符(与 ASCII 兼容):对于 Unicode 码点范围在U+0000到U+007F之间的字符,UTF - 8 编码与 ASCII 编码相同,使用 1 个字节表示,且最高位为 0。例如,字符 'A' 的 Unicode 码点是U+0041,其 UTF - 8 编码为41(十六进制),二进制表示为01000001。
  2. 双字节字符:对于 Unicode 码点范围在U+0080到U+07FF之间的字符,UTF - 8 使用 2 个字节表示。第一个字节的前两位固定为 11,第三位到第七位是该字符 Unicode 码点的一部分;第二个字节的前两位固定为 10,其余六位是该字符 Unicode 码点的另一部分。例如,字符 'è' 的 Unicode 码点是U+00E8,转换为二进制是00000000 11101000。按照 UTF - 8 编码规则,第一个字节为11000000 | 00001110 = C0 | 0E = CE,第二个字节为10000000 | 101000 = 80 | A0 = A0,所以 'è' 的 UTF - 8 编码为CE A0。
  3. 三字节字符:对于 Unicode 码点范围在U+0800到U+FFFF之间的字符,UTF - 8 使用 3 个字节表示。第一个字节的前三位固定为 111,第四位到第七位是该字符 Unicode 码点的一部分;后面两个字节的前两位都固定为 10,其余六位分别是该字符 Unicode 码点的其他部分。以汉字 “中” 为例,其 Unicode 码点是U+4E2D,二进制表示为01001110 00101101。UTF - 8 编码后的第一个字节为11100000 | 01001110 = E0 | 4E = E4,第二个字节为10000000 | 00101101 = 80 | 2D = BD,第三个字节为10000000 | 00000000 = 80 | 00 = A0,所以 “中” 的 UTF - 8 编码为E4 BD A0。
  4. 四字节字符:对于 Unicode 码点范围在U+10000到U+10FFFF之间的字符,UTF - 8 使用 4 个字节表示。第一个字节的前四位固定为 1111,第五位到第七位是该字符 Unicode 码点的一部分;后面三个字节的前两位都固定为 10,其余六位分别是该字符 Unicode 码点的其他部分。例如,一些表情符号就属于这一类。

UTF - 8 在 C++ string 中的存储

在 C++ 中,std::string本质上是一个字符序列,默认情况下,它以字节为单位存储字符。

当使用 UTF - 8 编码时,std::string可以自然地存储 UTF - 8 编码的字符串,因为 UTF - 8 编码的每个字节都可以直接存储在std::string的字符元素中。

例如:

 #include <iostream> #include <string> int main() { std::string utf8String = "你好,世界!"; for (char ch : utf8String) { std::cout << std::hex << static_cast<int>(static_cast<unsigned char>(ch)) << " "; } return 0; }

上述代码定义了一个包含中文字符的 UTF - 8 编码字符串utf8String,然后遍历该字符串,将每个字节以十六进制形式输出。由于 UTF - 8 编码的多字节特性,每个字符可能占用多个字节,在std::string中这些字节会依次存储。

UTF - 8 存储字符串的底层原理

在std::string内部,它维护着一块连续的内存空间,用于存放字符数据。

当存储 UTF - 8 编码的字符串时,这块内存就按照 UTF - 8 的字节序列依次填充。

比如存储 “hello” 这个字符串,每个字符都是 ASCII 字符,在 UTF - 8 中都只占 1 个字节,内存中会按顺序存放这 5 个字符对应的字节。

而当存储包含中文字符的字符串时,情况就变得复杂些。

以 “中国” 为例,“中” 的 UTF - 8 编码是E4 BD A0,“国” 的 UTF - 8 编码是E5 9B BD,在std::string的内存中,这 6 个字节会依次排列,从起始地址开始,先是 “中” 字的 3 个字节,紧接着是 “国” 字的 3 个字节。

中英文混合存储

当处理中英文混合的字符串时,UTF - 8 的变长特性就发挥了极大优势。

在std::string中,它依然是按照 UTF - 8 编码后的字节顺序依次存储。

例如字符串 “Hello,世界”,“Hello” 这 5 个英文字符,每个字符在 UTF - 8 中占用 1 个字节,共 5 个字节;英文逗号 “,” 在 UTF - 8 中占用 1 个字节;“世” 字占用 2 个字节,“界” 字占用 2 个字节'\0'占用1个字节。

所以整个字符串在std::string中存储时,从起始位置开始,先是 “H” 对应的字节,接着是 “e”“l”“l”“o”“,”“世”“界” 各自对应的字节,总共是 5 + 1 + 2 + 2 + 1 = 11 个字节。

下面通过代码来直观展示中英文混合存储的情况:

 #include <iostream> #include <string> int main() { std::string mixedString = "Hello,世界"; std::cout << "字符串长度(字节数): " << mixedString.size() << std::endl; for (size_t i = 0; i < mixedString.size(); ++i) { std::cout << std::hex << static_cast<int>(static_cast<unsigned char>(mixedString[i])) << " "; } return 0; }

 

运行上述代码,输出结果中字符串长度为 11,并且能看到每个字符对应的 UTF - 8 编码字节以十六进制形式呈现。

在实际处理中英文混合字符串时,我们要特别注意std::string的操作方法。

例如,若要在字符串中查找某个字符,对于英文字符,因为其在 UTF - 8 中占 1 个字节,直接按字节查找即可;

但对于中文字符,由于它可能占用 2 个字节,不能简单地按字节遍历查找,而是要按照 UTF - 8 编码规则,每次检查 2 个字节是否构成要查找的中文字符的 UTF - 8 编码。

在进行字符串截取操作时也同理,若要截取一个中文完整字符,必须保证截取的起始和结束位置都在该中文字符 UTF - 8 编码的字节范围内,否则可能导致截取后的字符串出现乱码。

总结

不同的编码规则在 C++ string的处理中各有特点和应用场景。

ASCII 编码简单但功能有限,Unicode 提供了全球统一的字符编码方案,而 UTF - 8、UTF - 16 和 UTF - 32 则是 Unicode 的不同编码实现方式。

UTF - 8 因其良好的兼容性和空间效率,成为了目前最常用的编码方式,特别是在互联网应用和跨平台开发中。

深入理解这些编码规则以及它们在 C++ string中的存储和处理方式,对于编写高质量的 C++ 代码至关重要。

在实际开发中,应根据具体需求选择合适的编码方式,以确保程序能够正确、高效地处理各种字符集的字符串。

本文完。

Read more

Flutter 三方库 shelf_modular 的鸿蒙化适配指南 - 掌控服务器路由资产、精密模块治理实战、鸿蒙级服务端专家

Flutter 三方库 shelf_modular 的鸿蒙化适配指南 - 掌控服务器路由资产、精密模块治理实战、鸿蒙级服务端专家

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 shelf_modular 的鸿蒙化适配指南 - 掌控服务器路由资产、精密模块治理实战、鸿蒙级服务端专家 在鸿蒙跨平台应用执行高级服务端管理与多维 Shelf 路由资产指控(如构建一个支持全场景秒级交互的鸿蒙大型全量后端服务中枢、处理海量 API Route Payloads 的语义认领或是实现一个具备极致指控能力的资产管理后台路由审计中心)时,如果仅仅依赖官方的基础 Shelf 处理器或者是极其繁琐的手动路由映射,极易在处理“由于模块嵌套导致的资产认领偏移”、“高频服务请求下的认领假死”或“由于多语言环境导致的符号解析冲突死结”时陷入研发代码服务端逻辑崩溃死循环。如果你追求的是一种完全对齐现代模块化标准、支持全量高度可定制路由(Modular-driven Backend)且具备极致指控确定性的方案。今天我们要深度解析的 shelf_modular——一个专注于解决“服务端资产标准化认领与模块化解耦”痛点的顶级工具库,正是帮你打造“鸿蒙超

By Ne0inhk
银发浪潮下的智能护理革命:全球老龄化社会护理机器人发展研究

银发浪潮下的智能护理革命:全球老龄化社会护理机器人发展研究

一、全球老龄化态势与护理需求激增 1.1 人口结构剧变下的养老挑战 当前,全球人口结构正经历着深刻变革,老龄化浪潮汹涌来袭。世界卫生组织数据清晰地勾勒出未来的图景:到 2050 年,全球 60 岁以上人口预计将飙升至 21 亿,老龄化率一举突破 25%。这一趋势在部分国家尤为显著,日本、韩国、德国等已深陷超深度老龄化的泥沼,养老问题成为社会发展的沉重负担。 以日本为例,这个高度发达的经济体,如今正面临着老龄化的严峻考验。其 65 岁以上人口占比接近 30%,每三个国民中就有一位老人。在街头巷尾,随处可见步履蹒跚的老人,他们的生活需求成为社会关注的焦点。韩国的老龄化速度同样惊人,从老龄化社会迈向超级老龄化社会仅仅用了短短 16 年,预计到 2050 年,65 岁以上人口占比将突破 40%,社会养老压力与日俱增。 而在我国,养老形势也不容乐观。截至 2024

By Ne0inhk

[ROS2实战] 从零打造SLAM机器人(一):基于ESP32与Micro-ROS的底盘运动控制与里程计实现

1. 前言:为什么SLAM需要一个好底盘? * SLAM的本质:SLAM 就像人闭着眼睛走路摸墙画地图。 * 底盘的作用: * 如果人走路晃晃悠悠(电机控制不稳),地图就会画歪。 * 如果人不知道自己迈了几步(里程计不准),地图就会断裂。 * 本篇目标:抛弃昂贵的工控机,用几十块钱的 ESP32 单片机,通过 Micro-ROS 接入 ROS 2 生态,实现精准的 PID闭环控制 和 里程计(Odometry)发布,为后续接入激光雷达做准备。 2. 系统架构设计 * 上位机:Ubuntu 22.04 (虚拟机) + ROS 2 Humble + Micro-ROS Agent。 * 下位机:ESP32 开发板 + 直流减速电机 + 霍尔编码器。 * 通信链路: * 物理层:

By Ne0inhk
无人机智能巡检系统-大疆上云api

无人机智能巡检系统-大疆上云api

1. 项目概述 1.1 项目名称 无人机智能巡检系统开发项目 1.2 项目背景 传统人工巡检方式存在效率低下、成本高昂、作业风险大等问题,尤其在大型设施(如机场、电力线路、油气管网)的巡检中面临诸多挑战。随着无人机技术、人工智能和物联网的快速发展,无人机智能巡检已成为提升巡检效率、降低运营成本、保障作业安全的理想解决方案。本项目旨在开发一套集实时监控、智能识别、任务管理、数据分析于一体的无人机智能巡检系统,满足现代化巡检工作的需求。 1.3 项目目标 * 开发一套完整的无人机智能巡检系统,实现巡检过程的自动化、智能化和可视化 * 集成AI识别功能,实现对异常事件(如人员入侵、设备故障、安全隐患)的实时检测与报警 * 构建统一的数据管理平台,实现巡检数据的集中存储、分析与共享 * 支持多类型无人机和巡检设备的无缝集成,确保系统的兼容性和可扩展性 * 提高巡检效率30%以上,降低人力成本40%,减少安全事故发生率 1.4

By Ne0inhk