一次讲清:宽字节、多字节、UTF-8、GBK、Unicode、代码页(从原理到 Windows/VC6 实战)

一次讲清:宽字节、多字节、UTF-8、GBK、Unicode、代码页(从原理到 Windows/VC6 实战)

admin
2025-12-27 / 0 评论 / 1 阅读 / 正在检测是否收录...

这篇文章的目标只有一个:建立不再被“乱码”反复折磨的工程级认知模型。全文从“字符是什么”讲到“Windows 为什么会乱”,再落到 VC6/老项目的可执行结论,一次性讲清,不靠零散记忆,不靠运气


一、乱码的本质只有一句话

乱码不是字符错了,而是“同一串字节,被用错了规则去解释”。

任何文本问题,都可以还原成下面这条链路:

字符含义 → 字符集 → 编码方式 → 字节序列 → 再被解释

只要编码方式和解释方式不一致,就一定乱码。


二、字符集(Character Set):先规定“有哪些字”

字符集解决的是:世界上有哪些字符,以及它们的编号是什么

常见字符集包括:ASCII、GB2312、GBK、GB18030,而现代世界几乎都以 Unicode 为核心。

Unicode 的本质不是编码,它只是一个全球统一的编号表(Code Point):

  • “中” → U+4E2D
  • “国” → U+56FD
  • 😀 → U+1F600

Unicode 只负责“编号”,不负责怎么存成字节


三、编码方式(Encoding):把编号变成字节

编码方式解决的是:Unicode 编号如何存储成二进制字节

UTF-8

  • Unicode 的一种编码方式
  • 1~4 字节变长
  • 完全兼容 ASCII
  • 网络和跨平台事实标准

例:“中”
Unicode:U+4E2D
UTF-8:E4 B8 AD(3 字节)

UTF-16

  • Unicode 的另一种编码方式
  • 2 或 4 字节
  • Windows 内部原生使用
  • C/C++ 中对应 wchar_t

例:“中”
UTF-16LE:2D 4E

GBK

  • 中文本地编码
  • 1 或 2 字节
  • 不是 Unicode
  • 强依赖“解释环境”

例:“中”
GBK:D6 D0


四、宽字节 vs 多字节(这是 Windows 语境)

这是Windows 编程术语,不是编码学术概念。

多字节(MBCS)

  • 使用 char*
  • 一个字符占用字节数不固定
  • GBK、UTF-8、Shift-JIS 都是多字节

多字节 ≠ UTF-8

宽字节(Wide)

  • 使用 wchar_t*
  • Windows 下固定为 UTF-16
  • 一个 wchar_t = 2 字节

五、ANSI 是什么(99% 乱码根源)

ANSI 不是一种编码。

在 Windows 里:

ANSI = “当前系统代码页下的多字节解释规则”

所有 xxxA API 的真实行为都是:
char* → 按当前代码页(CP_ACP) → 转 Unicode → 再处理


六、代码页(Code Page):解释字节的“字典”

代码页决定:char* 到底按什么规则被解释

常见代码页:

  • 936:GBK(简体中文)
  • 65001:UTF-8
  • 1252:西欧

CP_ACP 表示当前 ANSI 代码页,由系统区域设置决定。


七、Windows 并不存在“UTF-8 进程”

这是一个非常重要的纠正点。

Windows 内部统一使用 UTF-16(W API)
所谓“UTF-8 系统”,只是:

  • CP_ACP 改成了 65001
  • 所有 A API 按 UTF-8 解释 char*

W API 永远不受影响。


八、VC6 / 老项目乱码的形成公式

源码保存编码 ≠ 运行时 CP_ACP ≠ API 解释规则

乱码

典型炸点包括:

  • 源码是 GBK,系统 CP_ACP = 65001
  • 源码是 UTF-8,系统 CP_ACP = 936
  • DLL 被不同宿主进程加载
  • 使用 MessageBoxA / CreateFileA / RegSetValueExA

九、为什么 UTF-8 Beta 会“突然把老程序搞炸”

Windows 的 “Beta: 使用 Unicode UTF-8 提供全球语言支持” 会:

  • 把 CP_ACP 改成 65001
  • 所有 A API 改为按 UTF-8 解 char*

但老程序仍在传 GBK 字节,于是必然乱码。

系统没错,是老程序的隐含假设错了


十、工程级铁律(记住这 4 句就够)

  1. char* 没有编码,只有字节
  2. 编码信息存在于“解释方”,不在字符串本身
  3. A API = 按代码页猜
  4. W API = 唯一确定行为

十一、VC6 / 老项目的最终正确路线

在不重构整个项目的前提下,唯一长期稳定方案是:

  • 内部 char* 统一 UTF-8
  • 与系统交互全部使用 W API
  • 在边界做明确转换(MultiByteToWideChar / WideCharToMultiByte)
  • 不再依赖 CP_ACP
  • 不再关心宿主是 GBK 还是 UTF-8

这套方案不靠系统设置、不靠用户环境、不靠运气


十二、结语

乱码从来不是“中文太复杂”,而是规则被混用了
只要你在工程中做到:一处确定、处处确定,乱码问题会从此消失。

这篇文章本质上是在帮你建立一个判断标准:

“这串字节,是谁、按什么规则,在解释它?”

一旦这个问题你能随时回答,乱码就再也不是问题。

0

评论 (0)

取消