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

Windows 编程——字符串处理

Windows 字符串处理

字符串是存在编码的

​ 字符串存在编码,一般而言,咱们的起点是ASCII字符串编程,到后面,伴随我们实际上接触一定的中文显示,就需要设计到字符串编码的问题。常见的字符串的编码如下:

ASCII / ANSI / 多字节(MBCS) / Code Page(代码页)

Windows 里的“ANSI”通常不是单一编码,而是指**“系统 ANSI 代码页”**(CP_ACP),也就是 Windows 上为非 Unicode 程序指定的 8 位或多字节编码(例如在简体中文 Windows 上通常是 GBK/CP936)。不同地区或机器上 CP_ACP 可能不同。

GB2312 / GBK / GB18030(汉字家族)
  • GB2312:早期简体中文国家标准,子集。
  • GBK:对 GB2312 的扩展,包含更多汉字(微软的 code page 936 早期用于支持 GBK)。
  • GB18030:新标准,向后兼容并支持四字节序列,微软在 Windows 中以 code page 54936 支持。简体中文系统常用 CP 936/GBK(历史/兼容),而现代建议以 Unicode(UTF-8/UTF-16)为主。
Unicode / UTF-8 / UTF-16(Windows 的首选)
  • Unicode 是字符集(字符到码点的集合);UTF-8/UTF-16/UTF-32 是其编码方式。
  • Windows 内部广泛使用 UTF-16(Little Endian),也就是 WCHAR / wchar_t(在 Windows 下通常是 16 位)。Windows API 的“wide”(宽字符)系列使用 UTF-16。对于多字节/外部文本,Windows 提供代码页转换 API。

BOM(字节顺序标记)

常见 BOM:UTF-8 = EF BB BF;UTF-16 LE = FF FE;UTF-16 BE = FE FF。BOM 对检测与互操作很有帮助,但并非强制要求(尤其 UTF-8 通常不需要 BOM)。


Windows API vs C 运行库(CRT)——两条不同但会交叉的路线

Win32 API(推荐用于与操作系统交互)

​ 对于Windows,我们已经存在抽象宏自动根据编程环境进行字符编码的选择了。Windows 主要 API 提供 A(ANSI / multibyte)和 W(wide / UTF-16)两套函数:例如 CreateFileA / CreateFileW,头文件里通常有宏 CreateFile 根据 _UNICODE/UNICODE 定义映射到 AW。一般推荐直接调用 W 版本以避免编码/转换问题。

字符串-编码转换的核心 Win32 API:MultiByteToWideChar(多字节 -> UTF-16)和 WideCharToMultiByte(UTF-16 -> 多字节)。这两者支持选择 code page(如 CP_UTF8CP_ACP93654936 等)以及错误标志(如 MB_ERR_INVALID_CHARS)。这两个 API 的文档应作为转换工作的权威参考。

C 运行库(CRT)

CRT 也提供多字节与宽字符 API(mbstowcs / wcstombs / setlocale / _wcs* / _wfopen 等),这些通常基于当前 C locale(例如 setlocale(LC_CTYPE, ...))或 CRT 内部的 MBCS 设置。CRT 的多字节函数受 C locale 影响,且行为受实现与 locale 的影响;因此在 Windows 环境中做跨 code page 的可靠转换时,更推荐直接使用 Win32 的 MultiByteToWideChar / WideCharToMultiByte


A 后缀 与 W 后缀:具体是什么?应该使用哪个?

后缀 A:表示 ANSI/multibyte 版本(char*,按照某个 code page 解释字节序列)。

后缀 W:表示 wide(宽字符,UTF-16)的版本(wchar_t* / WCHAR*)。

还有不带后缀的宏(如 CreateFile):根据是否定义 UNICODE/_UNICODE,编译时映射到 CreateFileWCreateFileA;这称为“generic-text mappings”(TCHAR_T() 等)。尽量直接使用 W 版本避免潜在的编码陷阱。

为什么优先用 W

文件系统与许多内部 API 在 NT 内部是用 Unicode(UTF-16)表示路径与字符串,调用 A 版本会触发隐式从 ANSI code page 到 UTF-16 的转换(这可能丢失信息或在不同机器上行为不同)。W 直接接受 UTF-16,不依赖系统 ANSI code page。

Coding Example

从 UTF-8(或任意 code page)转 UTF-16(std::wstring / WCHAR*

#include <windows.h>
#include <string>
#include <vector>
#include <stdexcept>std::wstring MultiByteToWString(const std::string& s, UINT codePage = CP_UTF8) {if (s.empty()) return {};// 第一次获取需要的 wchar 数int required = MultiByteToWideChar(codePage, MB_ERR_INVALID_CHARS, s.data(), (int)s.size(), NULL, 0);if (required == 0) {DWORD err = GetLastError();throw std::runtime_error("MultiByteToWideChar failed: " + std::to_string(err));}std::vector<wchar_t> buf(required + 1);int converted = MultiByteToWideChar(codePage, MB_ERR_INVALID_CHARS, s.data(), (int)s.size(), buf.data(), required);if (converted == 0) {DWORD err = GetLastError();throw std::runtime_error("MultiByteToWideChar failed: " + std::to_string(err));}return std::wstring(buf.data(), converted);
}

说明:对 UTF-8 使用 CP_UTF8,并且在转换时推荐使用 MB_ERR_INVALID_CHARS 以便遇到非法序列能获取错误(某些 code page/flags 组合有限制,参见文档)。

从 UTF-16 到指定 code page(比如 CP_ACP 或 CP_UTF8)

std::string WideStringToMultiByte(const std::wstring& w, UINT codePage = CP_UTF8) {if (w.empty()) return {};int required = WideCharToMultiByte(codePage, 0, w.data(), (int)w.size(), NULL, 0, NULL, NULL);if (required == 0) {DWORD err = GetLastError();throw std::runtime_error("WideCharToMultiByte failed: " + std::to_string(err));}std::string buf(required, '\0');int converted = WideCharToMultiByte(codePage, 0, w.data(), (int)w.size(), &buf[0], required, NULL, NULL);if (converted == 0) {DWORD err = GetLastError();throw std::runtime_error("WideCharToMultiByte failed: " + std::to_string(err));}return buf;
}

说明:写入 CP_ACP 会把 Unicode 串转换为当前系统 ANSI code page 的编码(可能丢失一些字符),写入 CP_UTF8 会得到 UTF-8 编码。


DLL 导出(Export):ANSI 与 Unicode 的区分与兼容策略

导出符号本身
  • DLL 的导出名是编译后生成的符号名(C++ 会有 name-mangling,extern "C" 可避免)。编译时如果你在代码中写 CreateFile(宏),那宏被展开成 CreateFileWCreateFileA;但编译后的导出符号是具体的名字(MyFuncAMyFuncW)。因此 编译时的宏不会自动产生两个符号供外部选择。如果你希望 DLL 同时提供 ANSI 和 Unicode 接口,必须分别导出两种版本(MyApiAMyApiW),或在 .def 文件 / __declspec(dllexport) 中明确导出别名。
推荐做法
  1. 内部实现以 UTF-16 为主W),在需要同时支持旧客户端时,提供 A 的薄包装(在 A 版本中调用 MultiByteToWideChar(CP_ACP, ...) 将参数转换为 UTF-16,然后调用 W 实现;返回字符串时用 WideCharToMultiByte 转回)。
  2. 对外文档明确列出 API 字符编码。若你的 DLL 将被 .NET/Delphi/其他语言调用,文档需声明参数字符串的编码(例如“参数为 UTF-16LE 的 wchar_t*” 或“参数为 UTF-8 的 char*”)。对 P/Invoke,CharSetAnsi/Unicode/Auto)会决定字符串如何被封送(marshalled)。
导出样例(最简单的做法:导出两个显式名字)
extern "C" __declspec(dllexport) BOOL MyApiW(LPCWSTR text) {// 真实实现(UTF-16)
}extern "C" __declspec(dllexport) BOOL MyApiA(LPCSTR text) {// 将 ANSI 转为 UTF-16 然后调用 MyApiWstd::wstring w = MultiByteToWString(text, CP_ACP);return MyApiW(w.c_str());
}

或者通过 .def 文件把同一实现导出为两个名字(或使用别名)。有关 .def 文件和 EXPORTS 的官方说明参见 MS 文档。

Reference

  • Windows 字符串与 Unicode 介绍(UTF-16 / WCHAR):Working with Strings(Microsoft Learn)。(Microsoft 学习)
  • MultiByteToWideChar(多字节 -> UTF-16)文档。(Microsoft 学习)
  • WideCharToMultiByte(UTF-16 -> 多字节)文档。(Microsoft 学习)
  • 使用 BOM(Byte Order Marks)的说明。(Microsoft 学习)
  • Code Page 与编号(例如 CP 936 = GBK,54936 = GB18030,65001 = UTF-8 等):Code Page Identifiers / Code pages。(Microsoft 学习)
  • Generic-Text(TCHAR / _T / _UNICODE)与 A/W 映射说明。(Microsoft 学习)
  • IsTextUnicode(检测是否为 Unicode 的启发式 API)说明。(Microsoft 学习)
  • 控制台与 code page(GetConsoleOutputCP / SetConsoleOutputCP):Windows Console 文档。(Microsoft 学习)
  • 关于在 Windows 中使用 UTF-8 的额外说明(例如 MultiByteToWideChar 的 flags 使用注意):Use UTF-8 code pages in Windows apps。(Microsoft 学习

文章转载自:

http://i3p6FoUN.wjhqd.cn
http://P42ZaIMe.wjhqd.cn
http://qsznfWH3.wjhqd.cn
http://KEHPOLLZ.wjhqd.cn
http://OLxrU2g3.wjhqd.cn
http://OAfwXDpf.wjhqd.cn
http://jzwywnGS.wjhqd.cn
http://rTRMKkRL.wjhqd.cn
http://Sw4xXptl.wjhqd.cn
http://oanMUeT9.wjhqd.cn
http://0R6Iw8HB.wjhqd.cn
http://sTQejK0x.wjhqd.cn
http://03SSdbDq.wjhqd.cn
http://AMvJ51wq.wjhqd.cn
http://hkGczSmQ.wjhqd.cn
http://R0vJd9v8.wjhqd.cn
http://6QHlPLjs.wjhqd.cn
http://hjk2jf2i.wjhqd.cn
http://mOASD90G.wjhqd.cn
http://fkwLlFXb.wjhqd.cn
http://9wtbxzMi.wjhqd.cn
http://zDs0gUeg.wjhqd.cn
http://wR8Tq0ep.wjhqd.cn
http://IXRwoavp.wjhqd.cn
http://Mrto5Ht8.wjhqd.cn
http://EaVzbePV.wjhqd.cn
http://2b4vH0iB.wjhqd.cn
http://JmJeXgis.wjhqd.cn
http://RzaabR0O.wjhqd.cn
http://fFUmGQRW.wjhqd.cn
http://www.dtcms.com/a/366407.html

相关文章:

  • ReAct模式解读
  • 学会 Java 异常处理,其实没你想的那么难
  • 学习PaddlePaddle--环境配置-Windows 11 + RTX 4060
  • 渐变背景色和渐变字体颜色的实现方法
  • 美团开源龙猫大模型,与DeepSeek V3同一梯队?
  • 让B站视频4倍速度播放
  • Redis C++ 实现笔记(F篇)
  • 23种设计模式-Proxy模式
  • 无限时长视频生成新突破!复旦联合微软、腾讯混元推出StableAvatar,仅需1张照片+1段音频实现真人说话视频
  • 在 Debian 系统上清理缓存的方式和具体操作方法
  • Flink反压问题
  • 视频增强AI哪个效果好?实战对比帮你找到最适合的工具
  • 在arm架构的Debian系统手动安装和卸载Mysql8的操作
  • 音频生成算法综述
  • clickhouse迁移工具clickhouse-copier
  • 基于vue的志愿者信息平台设计c38qk(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 对接旅游行业安全需求:旅游安全急救实训室的功能构建与育人目标
  • APM32芯得 EP.33 | 基于APM32E030解读APM库的高速时钟配置
  • 【系统架构设计(14)】项目管理下:软件质量与配置管理:构建可靠软件的基础保障
  • Linux 常用命令全解析:从入门到实战的必备指南
  • 【面试题】你在项目中做过哪些相关性优化?
  • C#某公司面试题(含题目和解析)--1
  • Kafka如何保证高可用
  • aippt自动生成工具有哪些?一文看懂,总有一款适合你!
  • 【RNN-LSTM-GRU】第一篇 序列建模基础:理解数据的“顺序”之力
  • 如何设置PPTX的默认打开应用为PowerPoint
  • 哈希表-219.存在重复元素II-力扣(LeetCode)
  • C++ STL 中 `std::list` 双向链表容器的几个关键成员函数:`empty()`、`front()` 和 `pop_front()`
  • leetcode_141 环形链表
  • 【LeetCode】22、括号生成