代碼頁

代碼頁是字符編碼的別名，是特定語言的字符集的一張表。

## 歷史

早期，代碼頁是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"。

隨著圖形使用者界面作業系統的廣泛使用（最初被廣為接受的是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