字符集和字符編碼知識梳理

0 前言

在平時的開發(fā)過程中大部分人應該都遇到過中文亂碼問題,瀏覽網(wǎng)頁時也會遇到內(nèi)容顯示亂碼的情況解愤,一般遇到這種情況我們想到的可能是編碼問題镇饺。那我們說的編碼具體是指什么,亂碼問題的根本原因是什么送讲,又該如何解決呢奸笤?
答案的關(guān)鍵就是本文接下來要介紹的字符集與字符編碼。

1 概述

首先介紹一下字符哼鬓,字節(jié)揭保,字符串,字符集和字符編碼等基本概念魄宏。

  1. 字符(Character): 各種文字和符號的總稱,包括各國家文字存筏、標點符號宠互、圖形符號、數(shù)字等椭坚。
  2. 字節(jié)(Byte): 計算機信息技術(shù)用于計量存儲容量的一種計量單位予跌,也表示一些計算機編程語言中的數(shù)據(jù)類型和語言字符。
  3. 字符串(string): 一個連續(xù)的字符序列善茎,在存儲上類似于字符數(shù)組券册。
  4. 字符集(Character Set): 多個字符的集合,字符集種類較多垂涯,每個字符集包含的字符個數(shù)不同烁焙。
  5. 字符編碼(Character encoding): 也稱字集碼,是把字符集中的字符編碼為指定集合中某一對象(例如:比特模式耕赘、自然數(shù)序列骄蝇、8位組或者電脈沖),以便文本在計算機中存儲和通過通信網(wǎng)絡的傳遞操骡。
  6. 單字節(jié)字符集(Single Byte Character Set, SBCS): 所有字符都只用一個字節(jié)表示九火。用一個字節(jié)表示的0來標志SBCS字符串的結(jié)束赚窃。
  7. 多字節(jié)字符集(Multi-Byte Character Set, MBCS):部分字符用一個字節(jié)表示,部分字符用兩個或更多字節(jié)表示岔激。Windows中的MBCS包含兩種字符勒极,單字節(jié)字符(Single-Byte Characters)和雙字節(jié)字符(Double-Byte Characters)。有一些特定的值被保留用來表明它們是雙字節(jié)字符的一部分虑鼎。MBCS字符串也使用單字節(jié)的0來標志字符串結(jié)束辱匿。
  8. Unicode字符集: 通常又稱為寬字符集(Wide Character Set),所有字符都用兩個字節(jié)來表示震叙。 注意掀鹅,不要混淆Unicode字符集與MBCS,Unicode字符串采用兩個字節(jié)表示的0作為結(jié)束標志媒楼。

常見的字符集有:ASCII字符集乐尊、GB2312字符集、GBK字符集划址、Big5字符集扔嵌、GB18030字符集、Unicode字符集等夺颤。

一般情況下一個字符集對應一種字符編碼痢缎,但是Unicode比較特殊,存在多種字符編碼標準世澜,比如:UTF-7独旷,UTF-8UTF-16寥裂,UTF-32等嵌洼。

根據(jù)各個字符集的特性及發(fā)展歷程可以將其劃分成三類,如下圖所示:


常見字符集及字符編碼

上圖中只列舉了幾種常見的字符集與字符編碼封恰,更多內(nèi)容請參閱字符編碼麻养。

注意: 平時與人溝通的時候要弄清楚自己說的是字符集還是字符編碼,尤其是在談論Unicode的時候诺舔。

2 ASCII

  • ASCII(American Standard Code for Information Interchange鳖昌,美國信息互換標準編碼)是基于羅馬字母表的一套電腦編碼系統(tǒng)。
  • 包含了英文大小寫字符低飒、阿拉伯數(shù)字和西文符號等可顯示字符以及回車鍵许昨、退格、換行鍵等控制字符褥赊。
  • 主要用于顯示現(xiàn)代英語和其他西歐語言车要,是現(xiàn)今最通用的單字節(jié)編碼系統(tǒng),并等同于國際標準ISO 646崭倘。
  • 基本字符集采用7位(bits)表示一個字符翼岁,共128個字符类垫,字符值從0到127,其中32到126是可打印字符琅坡。
  • 擴展字符集采用8位(bits)表示一個字符悉患,共256個字符,增加了表格符號榆俺、計算符號售躁、希臘字母和特殊的拉丁符號,可以表示更多的歐洲常用字符茴晋。

3 ANSI(GB2312, GBK, Big5, GB18030)

隨著計算機的不斷普及陪捷,原來的ASCII單字節(jié)編碼已經(jīng)無法滿足世界各地的字符表示要求,于是诺擅,各個國家和地區(qū)都設(shè)計了一系列滿足于本國和地區(qū)的字符集與字符編碼市袖。
以中國為例,為了滿足國內(nèi)計算機使用漢字的需求烁涌,中國國家標準總局發(fā)布了一系列的漢字字符集國家標準編碼苍碟,統(tǒng)稱為GB碼,或國標碼撮执。

3.1 GB2312

GB2312是一個簡體中文字符集微峰,采用了二維矩陣編碼法對所有字符進行編碼:

  1. 首先構(gòu)造一個94行94列的方陣,對每一行稱為一個“區(qū)”抒钱,每一列稱為一個“位”蜓肆,
  2. 然后將所有字符依照下表的規(guī)律填寫到方陣中。
分區(qū)范圍 符號類型
第01區(qū) 中文標點谋币、數(shù)學符號以及一些特殊字符
第02區(qū) 各種各樣的數(shù)學序號
第03區(qū) 全角西文字符
第04區(qū) 日文平假名
第05區(qū) 日文片假名
第06區(qū) 希臘字母表
第07區(qū) 俄文字母表
第08區(qū) 中文拼音字母表
第09區(qū) 制表符號
第10-15區(qū) 無字符
第16-55區(qū) 一級漢字(以拼音字母排序)
第56-87區(qū) 二級漢字(以部首筆畫排序)
第88-94區(qū) 無字符

這樣所有的字符在方陣中都有一個唯一的位置仗扬,這個位置可以用區(qū)號、位號合成表示瑞信,稱為字符的區(qū)位碼。

GB2312編碼采用兩個字節(jié)表示一個漢字穴豫,區(qū)碼和位碼分別占用一個字節(jié)凡简。由于區(qū)碼和位碼的取值范圍都是在1-94之間,同西文的存儲表示沖突精肃。為了與西文進行區(qū)別秤涩,存儲時將區(qū)位碼的每個字節(jié)分別加上A0H(160)轉(zhuǎn)換為存儲碼。以漢字“啊”為例司抱,區(qū)位碼為1601(1001H)筐眷,存儲碼為B0A1H,轉(zhuǎn)換過程如下:

區(qū)位碼 區(qū)碼轉(zhuǎn)換 位碼轉(zhuǎn)換 存儲碼
1001H 10H+A0H=B0H 01H+A0H=A1H B0A1H

3.2 GBK

  • GBK是GB2312的擴展习柠,K為擴展的漢語拼音中“擴”字的聲母匀谣。英文全稱Chinese Internal Code Specification照棋。
  • 字符有一字節(jié)和雙字節(jié)編碼,00–7F范圍內(nèi)是第一個字節(jié)武翎,和ASCII保持一致烈炭,此范圍內(nèi)嚴格上說有96個文字和32個控制符號。
  • 之后的雙字節(jié)中宝恶,前一字節(jié)是雙字節(jié)的第一位符隙。總體上說第一字節(jié)的范圍是81–FE(也就是不含80和FF)垫毙,第二字節(jié)的一部分領(lǐng)域在40–7E霹疫,其他領(lǐng)域在80–FE。編碼范圍如下所示:
GBK編碼范圍
  • 雙字節(jié)符號可以表達的64K空間如下圖所示综芥。綠色和黃色區(qū)域是GBK的編碼丽蝎,紅色是用戶定義區(qū)域。沒有顏色區(qū)域是不正確的代碼組合毫痕。
GBK編碼空間

3.3 Big5

  • Big5又稱為大五碼或五大碼征峦,是一種繁體字編碼,主要在臺灣消请,香港和澳門等使用繁體字的地區(qū)使用栏笆。
  • Big5采用雙字節(jié)表示一個字符,第一個字節(jié)稱為“高位字節(jié)”臊泰,第二個字節(jié)稱為“低位字節(jié)”蛉加。
  • “高位字節(jié)”范圍0x81-0xFE,“低位字節(jié)”范圍0x40-0x7E缸逃,及0xA1-0xFE针饥。具體分區(qū)如下所示:
分區(qū) 備注
0x8140-0xA0FE 保留給用戶自定義字符(造字區(qū))
0xA140-0xA3BF 標點符號、希臘字母及特殊符號需频,包括在0xA259-0xA261丁眼,安放了九個計量用漢字:兙兛兞兝兡兣嗧瓩糎。
0xA3C0-0xA3FE 預留昭殉。此區(qū)沒有開放作造字區(qū)用苞七。
0xA440-0xC67E 常用漢字,先按筆劃再按部首排序挪丢。
0xC6A1-0xC8FE 保留給用戶自定義字符(造字區(qū))
0xC940-0xF9D5 次常用漢字巩剖,亦是先按筆劃再按部首排序灵嫌。
0xF9D6-0xFEFE 保留給用戶自定義字符(造字區(qū))

3.4 GB18030

GB18030是我國目前最新的變長多字節(jié)字符集,兼容GB2312,GBK以及Unicode3.1榆苞。主要特點如下:

  • 采用變長多字節(jié)編碼,每個字可以由1個、2個或4個字節(jié)組成。
  • 編碼空間龐大融柬,最多可定義161萬個字符。
  • 支持中國國內(nèi)少數(shù)民族文字姥闭,不需要動用造字區(qū)丹鸿。
  • 漢字收錄范圍包含繁體漢字以及日韓漢字。

GB18030包含三種長度的編碼:單字節(jié)的ASCII棚品、雙字節(jié)的GBK(略帶擴展)靠欢、以及用于填補所有Unicode碼位的四字節(jié)UTF區(qū)段。編碼范圍如下圖所示:


GB18030編碼范圍

3.5 Unicode

不同的國家和地區(qū)制定了適用于本國和地區(qū)的字符表示標準铜跑,但是這些標準之間往往是不兼容的门怪,比如用GB18030編碼的文件通過阿拉伯文的編碼標準去解析,肯定是顯示一堆亂碼锅纺。同時掷空,隨著計算機科學和互聯(lián)網(wǎng)的不斷發(fā)展,軟件國際化逐漸成為了必然的趨勢囤锉。在此背景下坦弟,一種包含了世界各地絕大部分文字字符的通用字符集就應運而生了-Unicode字符集。

Unicode字符集是通用多八位編碼字符集(Universal Multiple-Octet Coded Character Set)的簡稱官地。它為每種語言中的每個字符設(shè)定了統(tǒng)一并且唯一的二進制編碼酿傍,以滿足跨語言、跨平臺進行文本轉(zhuǎn)換驱入、處理的要求赤炒。

下面簡單梳理一下Unicode的編碼方式與實現(xiàn)方式的相關(guān)知識。

3.5.1 編碼方式

Unicode存在兩種編碼方式亏较,分別是UCS-2UCS-4莺褒。

  1. UCS-2: 采用兩個字節(jié)編碼,理論上最多可以表示216(65536)個字符雪情。
  2. UCS-4: 采用四個字節(jié)編碼遵岩,理論上最多可以表示232(2147483648)個字符,完全可以涵蓋所有語言的字符巡通。
    • UCS-4根據(jù)最高位為0的最高字節(jié)分成2^7=128個group尘执。
    • 每個group再根據(jù)次高字節(jié)分為256個plane
    • 每個plane根據(jù)第3個字節(jié)分為256行 (rows)扁达,每行包含256個cells正卧。
    • group 0的plane 0被稱作Basic Multilingual Plane, 即BMP蠢熄。

將UCS-4的BMP去掉前面的兩個零字節(jié)就得到了UCS-2跪解。在UCS-2的兩個字節(jié)前加上兩個零字節(jié),就得到了UCS-4的BMP。而目前的UCS-4規(guī)范中還沒有任何字符被分配在BMP之外叉讥。

3.5.2 實現(xiàn)方式

Unicode的實現(xiàn)方式不同于編碼方式窘行。一個字符的Unicode編碼是確定的。但是在實際傳輸過程中图仓,由于不同系統(tǒng)平臺的設(shè)計不一定一致罐盔,以及出于節(jié)省空間的目的,對Unicode編碼的實現(xiàn)方式有所不同救崔。Unicode的實現(xiàn)方式稱為Unicode轉(zhuǎn)換格式(Unicode Transformation Format惶看,簡稱為UTF)。

常見的實現(xiàn)方式有UTF-8六孵,UTF-16纬黎,UTF-32等。

  1. UTF-8: 以8bits(1字節(jié))為單位對UCS進行編碼劫窒,可以用1到4個字節(jié)來表示一個字符本今,是一種字節(jié)變長度編碼方式。
  2. UTF-16: 以16bits(2字節(jié))為單位對UCS進行編碼主巍,可以用2字節(jié)或4字節(jié)來表示一個字符冠息,是一種字節(jié)變長度編碼方式。
  3. UTF-32: 以32bits(4字節(jié))為單位對UCS進行編碼孕索,用4字節(jié)來表示一個字符逛艰,是一種字節(jié)固定長度編碼方式。

UCS-2和UCS-4是編碼方案檬果,而UTF-x是編碼實現(xiàn)方式瓮孙,涉及到實際傳輸,所以需要考慮字節(jié)序問題选脊。
字節(jié)序(Byte Order Mark,BOM): 用于表示字節(jié)傳輸過程中的存儲方式杭抠,常見的實現(xiàn)方式及對應BOM如下所示:

UTF BOM
UTF-8 EF BB BF
UTF-16LE FF FE
UTF-16BE FE FF
UTF-32LE FF FE 00 00
UTF-32BE 00 00 FE FF

LE表示小端字節(jié)序,BE表示大端字節(jié)序恳啥。

3.5.3 UTF-8

由于UTF-16和UTF-32都存在空間浪費的情況偏灿,而UTF-8采用字節(jié)為單位的變長編碼方式,大大提高了空間利用率钝的,因此翁垂,UTF-8也是我們平時用的最多的編碼方式。

UTF-8的編碼規(guī)則有兩條:

  1. 對于單字節(jié)的符號硝桩,字節(jié)的第一位設(shè)為0沿猜,后面7位為這個符號的unicode碼。因此對于英語字母碗脊,UTF-8編碼和ASCII碼是相同的啼肩。
  2. 對于n字節(jié)的符號(n>1),第一個字節(jié)的前n位都設(shè)為1,第n+1位設(shè)為0祈坠,后面字節(jié)的前兩位一律設(shè)為10害碾。剩下的沒有提及的二進制位,全部為這個符號的unicode碼赦拘。

下表總結(jié)了編碼規(guī)則慌随,字母x表示可用編碼的位。

Unicode符號范圍(十六進制) UTF-8編碼方式(二進制)
0000 0000-0000 007F 0xxxxxxx
0000 0080-0000 07FF 110xxxxx 10xxxxxx
0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

4 字符編碼的應用

字符集與字符編碼相關(guān)的知識非常多躺同,上面只是簡單介紹了一些常見的字符集以及字符編碼阁猜。想要了解更多的知識可以點擊相關(guān)概念的鏈接進行深入研究。

接下來介紹一下平時開發(fā)中會涉及到編碼相關(guān)的一些知識點蹋艺。

4.1 代碼頁

代碼頁是字符集編碼的別名蹦漠,最早是IBM公司首先使用〕岛#可以將代碼頁理解為字符和字節(jié)數(shù)據(jù)的映射表笛园。
Windows中將支持的代碼頁用一個編號來表示。例如代碼頁936就是簡體中文GBK侍芝。
可以在DOS的CMD命令行下通過chcp命令進行查看和修改系統(tǒng)的代碼頁研铆。

# 查看代碼頁
C:\>chcp
活動代碼頁: 936

4.2 區(qū)域(Locale)設(shè)置

Microsoft為了適應世界各地的文化背景和使用習慣,在Winodows系統(tǒng)中設(shè)計了區(qū)域設(shè)置的功能州叠】煤欤可以通過控制面板->區(qū)域與語言選項進行系統(tǒng)Locale和用戶Locale設(shè)置,其中系統(tǒng)Locale決定代碼頁咧栗;用戶Locale決定數(shù)字逆甜、貨幣、時間和日期格式致板。設(shè)置界面如下圖所示:

用戶Locale

系統(tǒng)Locale

C++中有兩種方式可以設(shè)置區(qū)域信息交煞,如下:

  1. 通過setlocale函數(shù)在運行時設(shè)置區(qū)域信息。
  2. 通過#pragrma setlocale編譯指定來設(shè)置區(qū)域信息斟或,該指令在編譯時起作用素征。

4.3 VS中字符集設(shè)置

為了方便代碼的移植和統(tǒng)一,目前的開發(fā)環(huán)境一般都會采用Uincode字符集萝挤,在VS中可以通過Project->Properties->Configuration Properities->General->Character Set進行設(shè)置御毅,如下圖所示:

VS中字符集設(shè)置

4.4 C++中字符和字符串的相關(guān)知識

C++的新標準中引入了UTF-16和UTF-32編碼方式的字符,分別用小寫字母u和大寫字母U開頭來表示怜珍,同時也引入了更多的字符串類型與操作端蛆,直接看下MSDN提供的代碼示例:

#include <string>  
using namespace std::string_literals; // enables s-suffix for std::string literals  
  
int main()  
{  
    // Character literals  
    auto c0 =   'A'; // char  
    auto c1 = u8'A'; // char  
    auto c2 =  L'A'; // wchar_t  
    auto c3 =  u'A'; // char16_t  
    auto c4 =  U'A'; // char32_t  
  
    // String literals  
    auto s0 =   "hello"; // const char*  
    auto s1 = u8"hello"; // const char*, encoded as UTF-8  
    auto s2 =  L"hello"; // const wchar_t*  
    auto s3 =  u"hello"; // const char16_t*, encoded as UTF-16  
    auto s4 =  U"hello"; // const char32_t*, encoded as UTF-32  
  
    // Raw string literals containing unescaped \ and "  
    auto R0 =   R"("Hello \ world")"; // const char*  
    auto R1 = u8R"("Hello \ world")"; // const char*, encoded as UTF-8  
    auto R2 =  LR"("Hello \ world")"; // const wchar_t*  
    auto R3 =  uR"("Hello \ world")"; // const char16_t*, encoded as UTF-16  
    auto R4 =  UR"("Hello \ world")"; // const char32_t*, encoded as UTF-32  
  
    // Combining string literals with standard s-suffix  
    auto S0 =   "hello"s; // std::string  
    auto S1 = u8"hello"s; // std::string  
    auto S2 =  L"hello"s; // std::wstring  
    auto S3 =  u"hello"s; // std::u16string  
    auto S4 =  U"hello"s; // std::u32string  
  
    // Combining raw string literals with standard s-suffix  
    auto S5 =   R"("Hello \ world")"s; // std::string from a raw const char*  
    auto S6 = u8R"("Hello \ world")"s; // std::string from a raw const char*, encoded as UTF-8  
    auto S7 =  LR"("Hello \ world")"s; // std::wstring from a raw const wchar_t*  
    auto S8 =  uR"("Hello \ world")"s; // std::u16string from a raw const char16_t*, encoded as UTF-16  
    auto S9 =  UR"("Hello \ world")"s; // std::u32string from a raw const char32_t*, encoded as UTF-32  
} 

4.5 ANSI字符串與Unicode字符串相互轉(zhuǎn)換

Windows提供了一些列的API函數(shù)來操作字符串,包括獲取字符集信息酥泛,判斷是否是DBCS的起始字節(jié)以及ANSI字符串與Unicode字符串之間相互轉(zhuǎn)換等今豆。用的比較多的應該就是字符串轉(zhuǎn)換的API了侈沪,如下所示:

  1. MultiByteToWideChar: ANSI字符串轉(zhuǎn)換成Unicode字符串。
  2. WideCharToMultiByte: Unicode字符串轉(zhuǎn)換成ANSI字符串晚凿。

ANSI字符串又稱為多字節(jié)字符串,Unicode字符串又稱為寬字節(jié)字符串瘦馍。每個人的叫法習慣不同歼秽,知道對應的關(guān)系即可。

為了操作簡單情组,ATL提供了幾個宏用于字符串轉(zhuǎn)換燥筷,底層實現(xiàn)都是通過上述介紹的MultiByteToWideChar和WideCharToMultiByte兩個API。
平時用的最多的就是CA2T和CT2A院崇,這兩個宏中各個字母代表的含義如下所示:

字母 含義
C 目標類型必須是Const類型
A ANSI字符串
W Unicode字符串
T 通用字符串肆氓,當定義了_UNICODE宏時T表示W(wǎng),否則T表示A

使用這兩個宏的時候需要注意幾點:

  1. 作用域問題:CA2T和CT2A的轉(zhuǎn)換后的數(shù)據(jù)作用域只在當前行底瓣,即在下一行代碼中再去訪問轉(zhuǎn)換后的數(shù)據(jù)會出現(xiàn)不可預知的問題谢揪。如下代碼所示:
// 正確,F(xiàn)un函數(shù)使用轉(zhuǎn)換后的TCHAR
Fun(CA2T(szSrc, CP_UTF8));
// 正確捐凭,轉(zhuǎn)換后的數(shù)據(jù)賦值給strDes
CString strDes = CA2T(szSrc, CP_UTF8);
// 錯誤拨扶,轉(zhuǎn)換后的數(shù)據(jù)在下一行被釋放,即szDes指向的數(shù)據(jù)變成未知
TCHAR *szDes = CA2T(szSrc, CP_UTF8);
  1. 參數(shù)問題: 轉(zhuǎn)換過程中可以指定code page信息茁肠,下面是摘之winnls.h對應參數(shù)的描述:
//  Code Page Default Values.
#define CP_ACP          0           // default to ANSI code page
#define CP_OEMCP        1           // default to OEM  code page
#define CP_MACCP        2           // default to MAC  code page
#define CP_THREAD_ACP   3           // current thread's ANSI code page
#define CP_SYMBOL       42          // SYMBOL translations
#define CP_UTF7         65000       // UTF-7 translation
#define CP_UTF8         65001       // UTF-8 translation

下面以CA2T為例患民,我們來看下底層實現(xiàn)是如何運用這些參數(shù)的。

// 1. CA2T其實轉(zhuǎn)化為CA2W
#define CA2T CA2W

// 2. CA2W又是通過模板CA2WEX來實現(xiàn)
typedef CA2WEX<> CA2W;

// 3. 下面是CA2WEX<>的模板實現(xiàn):
template< int t_nBufferLength = 128 >
class CW2AEX
{
public:
    CW2AEX(_In_z_ LPCWSTR psz) throw(...) 
       :m_psz( m_szBuffer )
    {
        Init( psz, _AtlGetConversionACP() );
    }
    CW2AEX(_In_z_ LPCWSTR psz, _In_ UINT nCodePage) throw(...) 
       :m_psz( m_szBuffer )
    {
        Init( psz, nCodePage );
    }
    ~CW2AEX() throw()
    {       
        AtlConvFreeMemory(m_psz,m_szBuffer,t_nBufferLength);
    }

    _Ret_z_ operator LPSTR() const throw()
    {
        return( m_psz );
    }

private:
    void Init(_In_z_ LPCWSTR psz, _In_ UINT nConvertCodePage) throw(...)
    {
        if (psz == NULL)
        {
            m_psz = NULL;
            return;
        }
        int nLengthW = lstrlenW( psz )+1;        
        int nLengthA = nLengthW*4;
        
        AtlConvAllocMemory(&m_psz,nLengthA,m_szBuffer,t_nBufferLength);

        BOOL bFailed=(0 == ::WideCharToMultiByte( nConvertCodePage, 0, psz, nLengthW, m_psz, nLengthA, NULL, NULL ));
        if (bFailed)
        {
            if (GetLastError()==ERROR_INSUFFICIENT_BUFFER)
            {
                nLengthA = ::WideCharToMultiByte( nConvertCodePage, 0, psz, nLengthW, NULL, 0, NULL, NULL );
                AtlConvAllocMemory(&m_psz,nLengthA,m_szBuffer,t_nBufferLength);
                bFailed=(0 == ::WideCharToMultiByte( nConvertCodePage, 0, psz, nLengthW, m_psz, nLengthA, NULL, NULL ));
            }           
        }
        if (bFailed)
        {
            AtlThrowLastWin32();
        }
    }

public:
    LPSTR m_psz;
    char m_szBuffer[t_nBufferLength];

private:
    CW2AEX(_In_ const CW2AEX&) throw();
    CW2AEX& operator=(_In_ const CW2AEX&) throw();
};

inline UINT WINAPI _AtlGetConversionACP() throw()
{
#ifdef _CONVERSION_DONT_USE_THREAD_LOCALE
    return CP_ACP;
#else
    return CP_THREAD_ACP;
#endif
}
  • 從上述的代碼邏輯中可以看出垦梆,當沒有指定轉(zhuǎn)換代碼頁的時候默認通過_AtlGetConversionACP函數(shù)來獲取轉(zhuǎn)換參數(shù)匹颤。
  • 注意區(qū)別CP_ACP和CP_THREAD_ACP,大多數(shù)情況下這兩者對應的代碼頁是一樣的,都是系統(tǒng)當前的代碼頁托猩,但是如果程序在運行時指定了其它的代碼頁則會出現(xiàn)不一致的情況印蓖。
  • CP_ACP代表的是系統(tǒng)當前的代碼頁,但是不同系統(tǒng)中當前的代碼頁可能是不一樣的京腥,例如A電腦設(shè)置的是936(簡體中文)另伍,B電腦設(shè)置的是950(繁體中文),此時將一個簡體中文字符串進行轉(zhuǎn)換時在A電腦上可以成功绞旅,但是在B電腦上就出現(xiàn)了亂碼情況摆尝。因此,建議采用CP_UTF8參數(shù)對字符串進行轉(zhuǎn)換因悲,降低出現(xiàn)亂碼的概率堕汞。

更多詳細內(nèi)容請參閱ATL and MFC String Conversion Macros

5 總結(jié)

字符集和字符編碼相關(guān)的知識非常多晃琳,本文主要梳理總結(jié)了一些常用的讯检、比較核心的概念琐鲁,希望對大家有所幫助。

整理這篇文章的過程中查看了非常多的資料人灼,主要是維基百科和MSDN文檔围段,大部分可以通過文中超鏈接跳轉(zhuǎn)過去,還有一些博客對字符編碼的介紹投放,下面列舉一些個人覺得總結(jié)的不錯的文章作為補充閱讀奈泪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末烙样,一起剝皮案震驚了整個濱河市冯遂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谒获,老刑警劉巖蛤肌,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異批狱,居然都是意外死亡寻定,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門精耐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來狼速,“玉大人,你說我怎么就攤上這事卦停∠蚝” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵惊完,是天一觀的道長僵芹。 經(jīng)常有香客問我,道長小槐,這世上最難降的妖魔是什么拇派? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮凿跳,結(jié)果婚禮上件豌,老公的妹妹穿的比我還像新娘。我一直安慰自己控嗜,他們只是感情好茧彤,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疆栏,像睡著了一般曾掂。 火紅的嫁衣襯著肌膚如雪惫谤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天珠洗,我揣著相機與錄音溜歪,去河邊找鬼。 笑死许蓖,一個胖子當著我的面吹牛蝴猪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蛔糯,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼窖式!你這毒婦竟也來了蚁飒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤萝喘,失蹤者是張志新(化名)和其女友劉穎淮逻,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阁簸,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡爬早,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了启妹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筛严。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖饶米,靈堂內(nèi)的尸體忽然破棺而出桨啃,到底是詐尸還是另有隱情,我是刑警寧澤檬输,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布照瘾,位于F島的核電站,受9級特大地震影響丧慈,放射性物質(zhì)發(fā)生泄漏析命。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一逃默、第九天 我趴在偏房一處隱蔽的房頂上張望鹃愤。 院中可真熱鬧,春花似錦完域、人聲如沸昼浦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽关噪。三九已至鸟蟹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間使兔,已是汗流浹背建钥。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留虐沥,地道東北人熊经。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像欲险,于是被迫代替她去往敵國和親镐依。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容