代码页

代码页是字符编码的别名，是特定语言的字符集的一张表。

## 历史

早期，代码页是IBM称呼计算机的BIOS所支持的字符集编码。当时通用的操作系统都是命令行界面，这些操作系统直接使用BIOS提供的字符绘制功能来显示字符（或者是一组嵌入在显卡字符生成器中的字形）。这些BIOS代码页也被称为OEM代码页。图形操作系统使用自己的字符呈现引擎（rendering engine），可以支持多个不同的字符集编码，这类代码页被称作ANSI代码页。

早期IBM和微软内部使用数字来标记不同的编码字符集，不同的厂商对同一个字符集编码使用各自不同的名称。例如，UTF-8在IBM称作代码页1208，在微软称作代码页65001，在SAP称作代码页4110。

1987年4月，IBM发布了PC-DOS 3.3，正式开始使用16比特的无符号整数标识不同的代码页。这时的PC机使用CGA显示系统的字符界面，绘制不同语言的字符依靠BIOS硬件厂商（在当时就是指制定业界标准的IBM）提供的功能。如果想更换所支持的字符集，就必须换上支持该字符集的ROM芯片。微软作为DOS操作系统的软件厂商，并不拥有绘制这些字符集的知识产权。所以这些字符集的绘制实现，称作OEM代码页。最常见、最具代表性的OEM代码页是"IBM PC或MS-DOS 代码页437"。

<image>

随着图形用户界面操作系统的广泛使用（最初被广为接受的是Windows 3.1），操作系统具有了字符绘制的功能。微软在Windows操作系统没有转向UTF-16作为内码实现之前（也就是在Windows 2000之前），针对不同的使用地区与国家，定义了一系列的支持不同语言字符集的代码页，被称作"Windows（或ANSI）代码页"。代表性的是实现了ISO-8859-1的代码页1252。

## OEM（IBM PC）代码页

- 437—最初的IBM PC代码页，实现了扩展ASCII字符集
- 737—希腊语
- 850—“多语言（Latin-1）”（西欧语言）
- 852—“斯拉夫（Latin-2）”（中欧及东欧语言）
- 855—西里尔（Cyril）字母
- 857—土耳其语
- 858—带欧元符号的“多语言”
- 860—葡萄牙语
- 861—冰岛语
- 863—法语加拿大英语
- 865—北欧
- 866—西里尔（Cyril）字母
- 869—希腊语

代码页819实现了Latin-1（ISO/IEC 8859-1），用于IBM AS/400小型机。

### OEM代码页转换为ASCII代码页

对于中日韩的多字节编码的代码页，OEM代码页与ASCII代码页相同，例如对于简体中文的OEM代码页与ASCII代码页就是GBK代码页。而对于单字节编码的代码页，如英语、俄语等，OEM代码页与ASCII代码页一般不同。这是因为在MS-DOS时代，计算机只能使用字符界面在屏幕上画出表格的框线，所以OEM代码页要在单字节字符集中包含方框绘制字符；此外，OEM代码页437提供的有限的变音符号，只能覆盖法语、西班牙语、德语、意大利语、瑞典语的字母表。而在Windows的早期时代，仍然使用单字节字符集，这时就舍弃了这些不必要的方框绘制字符，取而代之的是丹麦语、挪威语、冰岛语、加拿大法语的变音符号。为此，一个用OEM代码页的字节流要在Windows上正确显示，就需要或者显式设定使用OEM代码页；或者要显式把OEM代码页的字节流转化为ASCII代码页的字符流，这需要使用Windows系统调用`OemToChar()`。

## Windows（ANSI）代码页

Windows代码页最初是根据ANSI草案实现的，这个草案最终成为ISO 8859-1。这是Windows代码页被称作ANSI的缘由。

- 874—泰语
- 1250—东欧拉丁字母
- 1251—古斯拉夫语
- 1252—西欧拉丁字母ISO-8859-1
- 1253—希腊语
- 1254—土耳其语
- 1255—希伯来语
- 1256—阿拉伯语
- 1257—波罗的海国家
- 1258—越南语

Windows-1252与ISO-8859-1并不完全一致。ISO-8859-1在0x80-0x9F范围的控制字符，在Windows-1252中被可打印字符取代。由于在web网页中，ASCII控制字符不起作用，所以网页一般用Windows-1252代码页标记替代ISO-8859-1标记。

## 中日韩语言代码页

既是OEM代码页，也是Windows代码页。

- 936—简体中文（GBK）
- 950—繁体中文（大五码）
- 951—繁体中文（大五码和HKSCS）
- 932—日文（Shift-JIS）
- 949—韩文（EUC-KR）
- 20000（CNS）以EUC编码的繁体中文CNS编码
- 20002（Eten）以EUC编码的繁体中文倚天码
- 20936（GB2312-80）以EUC编码的简体中文GB2312编码（老设备或嵌入式设备常见）
- 50227（ISO-2022-GB）简体中文的Esc序列编码，纯ASCII
- 50229（ISO-2022-CNS）繁体中文的Esc序列编码，纯ASCII
- 52936（HZ-GB-2312）以~{和~}分隔的简体中文GB2312编码，纯ASCII
- 54936—简体中文（GB18030）

## 其他代码页

- 10000—Macintosh Roman encoding（followed by several other Mac character sets）
- 10007—Macintosh Cyrillic encoding
- 10029—Macintosh Central European encoding
- 1200—UCS-2LE Unicode 小端序
- 1201—UCS-2BE Unicode 大端序
- 65000—UTF-7 Unicode
- 65001—UTF-8 Unicode

## Windows操作系统中使用的代码页

Windows平台上的GUI程序使用ANSI代码页，而在控制台程序使用OEM代码页（以便向后兼容）。这意味着，如果在记事本程序（notepad.exe）打开一个8位字符集编码的文本文件，将使用ANSI代码页；如果在命令行中用type命令显示这个文本文件的内容，将使用OEM代码页。这两个代码页在前128个字符的编码是一样的，但后128个字符的编码可能不一致。在Windows的命令行窗口通过标记、复制操作把一部分文本内容复制到记事本程序中，实际上是把Unicode格式的内容保存在剪贴板，使得这种文本复制保持了字符编码的透明转换。

对于Windows操作系统中的命令行窗口（Command Prompt），chcp命令在没有参数时，显示当前代码页；chcp命令带一个整数参数，则改变命令行窗口的当前代码页为参数所指定。

把UTF-8编码文本直接写到控制台，必须先使用函数SetConsoleOutputCP(65001)，然后使用puts一族的函数来输出文本。把UTF-8编码文本写入UTF-8文件时，可以直接使用窄字符输出函数。

在Windows API中，CP_ACP与CP_OEMCP分别表示当前系统的ANSI代码页与OEM代码页。对于CJK(多字节编码)的环境(泰文，日文，韩文，中文),CP_ACP与CP_OEMCP没有区别。对于非 CJK(单字节编码)的环境这两个代码页不同。Windows的文件操作的API默认使用ASCII代码页(即CP_ACP)，设备的操作的函数使用OEM代码页(即CP_OEMCP)。读写console的函数是对console设备的操作，所以默认使用OEMCP。

## 查询代码页的信息

Windows系统调用`GetCPInfo()`给出指定的代码页的信息。如东亚多字节代码页的缺省字符、前导字节的范围：

```c
{
 CPINFO info;
 UINT iCP = 932; //GBK
 GetCPInfo(iCP, &info);
 printf("Code page %d's default char is [%c]\n", iCP, info.DefaultChar[0]);
 printf("Max size of a char: %d\n", info.MaxCharSize);
 int i;
 const int iMaxLeadBytePairNum = 5;
 for (i = 0; i < iMaxLeadBytePairNum; i++)
 {
    if (info.LeadByte[i * 2] == 0 && info.LeadByte[i * 2 + 1] == 0)
        break;
    printf("Lead byte pair %d: 0x%02X-0x%02X\n", i, info.LeadByte[i * 2], info.LeadByte[i * 2 + 1]);
 }
}
```

## 外部链接

- IBM代码页
- IBM/ICU Charset Information
- Microsoft code page identifiers（Microsoft's list contains only code pages actively used by normal apps on Windows see also Torsten Mohrin's list for the full list of supported code pages）
- Shorter Microsoft list containing only the ANSI and OEM code pages but with links to more detail on each
- Character Sets And Code Pages At The Push Of A Button