C++ 字符处理、编码格式
C++ 字符处理、编码格式
- C/C++中存储字符串的库方法
- 为什么需要加_T()
引言:
在刚开始学编程的时候,就接触了打印字符串到终端,比如“hello world”;在这个过程中,有过一个问题,就是书写的字符串带中文时,打印出来是乱码。
这是因为,当我们在通过键盘输入文字时,编译器根据某种编码格式,将我们输入的文字存到字符串中,打印时,又将字符串中的数据取出来,通过某种编码格式转换到文字(显示到屏幕);在这个过程中,如果输入时的编码格式和显示时的编码格式不同,就会出现乱码。
编码的本质
计算机存储的字符串本质上是字节序列(如 char[] 或std::string),而人类可读的文字(如中文、英文)需要通过某种编码规则(如 UTF-8、GBK)转换为字节序列。
- 输入阶段:键盘输入的文字(如“你好”)会按当前环境的默认编码(如 UTF-8)转换为字节序列,存储在代码文件中。
- 编译阶段:编译器读取代码文件时,需要知道文件的编码格式(否则可能误判,尤其是非 ASCII 字符)。
- 输出阶段:终端或控制台在显示时,会按自身的编码规则解释这些字节。如果终端的编码与字符串的编码不一致,就会显示乱码。
在网络编程中,字符的处理涉及跨平台/跨系统,兼容性等问题。
如何查看系统的编码格式:
查看windows系统默认编码 修改windows系统默认编码
linux怎么查看编码格式
补充:
操作系统的编码和文本编辑器的编码
操作系统编码(如 Windows 的 ACP、Linux 的 LANG)决定了:
默认的字符集(如 Windows 中文版默认 GBK,Linux 通常 UTF-8)。
系统 API 的行为(如 MessageBoxA 按 ANSI 编码解析字符串)。文本编辑器编码决定的是:
文件在磁盘上的存储格式(如 UTF-8、GBK、UTF-16)。
如何解析文件内容(避免乱码)
为什么文本编辑器需要独立设置编码,而不是和操作系统的统一:
- 文件来源多样性:
不同文件可能由不同工具生成(如跨平台协作时,Linux 的 UTF-8 文件可能在 Windows 的 GBK 环境下打开)。
编辑器需要灵活适配,而非强制依赖系统编码。- 历史兼容性:
旧文件可能是 ANSI 编码(如 GBK),而新项目希望用 UTF-8,编辑器需支持多编码。- 用户控制权:
开发者可能需要显式指定编码(如强制保存为 UTF-8 with BOM),避免编译器或工具链的歧义。
C/C++中存储字符串的库方法
CString(MFC/ATL)
std::string
std::vector
std::vector<wchar_t>
std::wstring
std::u16string (C++11起)
std::u32string (C++11起)
std::string_view (C++17起)
QString (Qt框架)
glib::ustring (GLib/GTK)
说明:
类型 | 编码 | 所有权 | 可变性 | 典型用途 |
---|---|---|---|---|
CString | 依赖编译选项 | 拥有 | 可变 | Windows MFC/ATL开发 |
std::string | 字节(UTF-8) | 拥有 | 可变 | 通用文本处理 |
std::vector | 原始字节 | 拥有 | 可变 | 二进制数据存储 |
std::wstring | 平台宽字符 | 拥有 | 可变 | Windows Unicode API |
std::u16string | UTF-16 | 拥有 | 可变 | 明确UTF-16需求(如WebKit) |
std::u32string | UTF-32 | 拥有 | 可变 | 码点级操作(如分词算法) |
std::string_view | 同原数据 | 非拥有 | 只读 | 高性能只读访问 |
QString | UTF-16 | 拥有 | 可变 | Qt应用程序开发 |
glib::ustring | UTF-8 | 拥有 | 可变 | GTK/国际化文本处理 |
所有权: 能否自动管理字符串的生命周期。比如C语言的原始字符串方法char*,就是没有所有权的,不再使用字符串时,需要自己delete;
可变性: 能否改变字符串长度
- CString (MFC/ATL) 编码:依赖编译选项(_UNICODE宏决定是char或wchar_t)
内存:自动管理(基于引用计数)
特点:
Windows专用,深度集成MFC/ATL
自动处理ANSI/Unicode转换(如A2W/W2A)
场景:Windows桌面开发(MFC/ATL项目)
- std::string 编码:字节流(默认UTF-8但不强制)
内存:自动管理(RAII)
特点:
通用性强,支持动态扩容
无编码感知(纯字节容器)
场景:跨平台通用字符串操作(非Unicode敏感场景)
- std::vector 编码:原始字节(无编码概念)
内存:自动管理(类似std::string)
特点:
不自动添加’\0’,可存二进制数据
无字符串专用方法(如find)
场景:二进制数据与字符串混合处理(如网络协议)
- std::vector<wchar_t> 编码:宽字符(平台相关:Win=UTF-16,Linux=UTF-32)
内存:自动管理
特点:
类似std::vector但存储宽字符
需手动处理字符串逻辑
场景:需要宽字符的二进制操作(如Windows API交互)
- std::wstring 编码:宽字符(平台依赖)
内存:自动管理
特点:
跨平台行为不一致(Win=2字节/字符,Linux=4字节)
提供与std::string相同的接口
场景:Windows Unicode API调用(如MessageBoxW)
- std::u16string (C++11) 编码:UTF-16(固定char16_t,2字节/字符)
内存:自动管理
特点:
编码明确,跨平台一致
适合与UTF-16系统(如JavaScript)交互
场景:跨平台Unicode处理(避免wstring歧义)
- std::u32string (C++11) 编码:UTF-32(固定char32_t,4字节/字符)
内存:自动管理
特点:
每个字符固定4字节,内存占用大
适合需要码点级操作的情况
场景:字符级精确处理(如文本分析算法)
- std::string_view (C++17) 编码:与原字符串一致
内存:非拥有视图(无所有权)
特点:
零拷贝,高性能
只读,生命周期依赖原数据
场景:函数参数传递、临时字符串解析
- QString (Qt) 编码:UTF-16(内部使用QChar,2字节/字符)
内存:自动管理(隐式共享/写时复制)
特点:
提供丰富的Unicode和国际化支持
依赖Qt框架
场景:Qt应用程序开发(如界面文本、正则表达式)
- glib::ustring (GLib/GTK) 编码:UTF-8
内存:自动管理
特点:
集成GLib内存管理
支持GNU gettext国际化
场景:GTK/Linux桌面开发(如国际化文本渲染)
为什么需要加_T()
在C++中,_U、_T 这类宏通常用于字符串字面量的编码转换,目的是让同一段代码在不同平台或编码环境下正确处理字符串。
(1)字面量
是指直接在源代码中写出的常量值(固定的数据),比如_T(“你好”),"你好"就是字面量
(2)_T() 宏
用于修饰字符串字面量,使其在编译时根据字符编码设置(ANSI/Unicode)转换成合适的字符串类型
(2)为什么需要_T()宏?
编码兼容性:
Windows 历史遗留问题:ANSI(char)和 Unicode(wchar_t)API 并存。
_T 宏让同一代码适配两种模式。
(3)输入文字到编译的整个过程
1.键盘输入 → 输入法转换
当输入 “你好” 时,输入法将(拼音"ni hao")转换为目标字符(“你好”)。
这些字符会以当前系统的默认编码(如 Windows 的 GBK、Linux 的 UTF-8)转换为字节序列,存入编辑器缓冲区。
(4)VS中项目属性的字符集选项
- 影响_T()以及其它一些宏(比如TCHAR )的行为
- 如果字符串字面量没有显式标注了前缀,比如_T()宏,编译器就会按照设置的字符集属性来处理字面量
- 影响范围:编译时的字符串处理和 API 调用,不改变文件存储编码。
(5)除了_T()还有哪些宏?
宏/前缀 | 平台依赖 | 编码 | 典型用途 |
---|---|---|---|
_T | 是(Windows) | ANSI/Unicode | 兼容旧版 Windows 代码 |
_U | 否 | UTF-32 | 跨平台 Unicode 处理(C++11+) |
L | 是 | 宽字符(平台定) | Windows API 宽字符串 |
u8/u/U | 否 | UTF-8/16/32 | 现代 C++ 明确指定编码 |