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

0 前言

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

1 概述

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

  1. 字符(Character): 各種文字和符號的總稱,包括各國家文字、標(biāo)點符號、圖形符號、數(shù)字等。
  2. 字節(jié)(Byte): 計算機信息技術(shù)用于計量存儲容量的一種計量單位,也表示一些計算機編程語言中的數(shù)據(jù)類型和語言字符。
  3. 字符串(string): 一個連續(xù)的字符序列,在存儲上類似于字符數(shù)組。
  4. 字符集(Character Set): 多個字符的集合,字符集種類較多,每個字符集包含的字符個數(shù)不同。
  5. 字符編碼(Character encoding): 也稱字集碼,是把字符集中的字符編碼為指定集合中某一對象(例如:比特模式、自然數(shù)序列、8位組或者電脈沖),以便文本在計算機中存儲和通過通信網(wǎng)絡(luò)的傳遞。
  6. 單字節(jié)字符集(Single Byte Character Set, SBCS): 所有字符都只用一個字節(jié)表示。用一個字節(jié)表示的0來標(biāo)志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來標(biāo)志字符串結(jié)束。
  8. Unicode字符集: 通常又稱為寬字符集(Wide Character Set),所有字符都用兩個字節(jié)來表示。 注意,不要混淆Unicode字符集與MBCS,Unicode字符串采用兩個字節(jié)表示的0作為結(jié)束標(biāo)志。

常見的字符集有:ASCII字符集、GB2312字符集、GBK字符集、Big5字符集、GB18030字符集、Unicode字符集等。

一般情況下一個字符集對應(yīng)一種字符編碼,但是Unicode比較特殊,存在多種字符編碼標(biāo)準(zhǔn),比如:UTF-7UTF-8,UTF-16UTF-32等。

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


常見字符集及字符編碼

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

注意: 平時與人溝通的時候要弄清楚自己說的是字符集還是字符編碼,尤其是在談?wù)揢nicode的時候。

2 ASCII

  • ASCII(American Standard Code for Information Interchange,美國信息互換標(biāo)準(zhǔn)編碼)是基于羅馬字母表的一套電腦編碼系統(tǒng)。
  • 包含了英文大小寫字符、阿拉伯?dāng)?shù)字和西文符號等可顯示字符以及回車鍵、退格、換行鍵等控制字符。
  • 主要用于顯示現(xiàn)代英語和其他西歐語言,是現(xiàn)今最通用的單字節(jié)編碼系統(tǒng),并等同于國際標(biāo)準(zhǔn)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)計算機使用漢字的需求,中國國家標(biāo)準(zhǔn)總局發(fā)布了一系列的漢字字符集國家標(biāo)準(zhǔn)編碼,統(tǒng)稱為GB碼,或國標(biāo)碼。

3.1 GB2312

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

  1. 首先構(gòu)造一個94行94列的方陣,對每一行稱為一個“區(qū)”,每一列稱為一個“位”,
  2. 然后將所有字符依照下表的規(guī)律填寫到方陣中。
分區(qū)范圍 符號類型
第01區(qū) 中文標(biāo)點、數(shù)學(xué)符號以及一些特殊字符
第02區(qū) 各種各樣的數(shù)學(xué)序號
第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之間,同西文的存儲表示沖突。為了與西文進(jìn)行區(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)嚴(yán)格上說有96個文字和32個控制符號。
  • 之后的雙字節(jié)中,前一字節(jié)是雙字節(jié)的第一位??傮w上說第一字節(jié)的范圍是81–FE(也就是不含80和FF),第二字節(jié)的一部分領(lǐng)域在40–7E,其他領(lǐng)域在80–FE。編碼范圍如下所示:
GBK編碼范圍
  • 雙字節(jié)符號可以表達(dá)的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 標(biāo)點符號、希臘字母及特殊符號,包括在0xA259-0xA261,安放了九個計量用漢字:兙兛兞兝兡兣嗧瓩糎。
0xA3C0-0xA3FE 預(yù)留。此區(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ū)的字符表示標(biāo)準(zhǔn),但是這些標(biāo)準(zhǔn)之間往往是不兼容的,比如用GB18030編碼的文件通過阿拉伯文的編碼標(biāo)準(zhǔn)去解析,肯定是顯示一堆亂碼。同時,隨著計算機科學(xué)和互聯(lián)網(wǎng)的不斷發(fā)展,軟件國際化逐漸成為了必然的趨勢。在此背景下,一種包含了世界各地絕大部分文字字符的通用字符集就應(yīng)運而生了-Unicode字符集。

Unicode字符集是通用多八位編碼字符集(Universal Multiple-Octet Coded Character Set)的簡稱。它為每種語言中的每個字符設(shè)定了統(tǒng)一并且唯一的二進(jìn)制編碼,以滿足跨語言、跨平臺進(jìn)行文本轉(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進(jìn)行編碼,可以用1到4個字節(jié)來表示一個字符,是一種字節(jié)變長度編碼方式。
  2. UTF-16: 以16bits(2字節(jié))為單位對UCS進(jìn)行編碼,可以用2字節(jié)或4字節(jié)來表示一個字符,是一種字節(jié)變長度編碼方式。
  3. UTF-32: 以32bits(4字節(jié))為單位對UCS進(jìn)行編碼,用4字節(jié)來表示一個字符,是一種字節(jié)固定長度編碼方式。

UCS-2和UCS-4是編碼方案,而UTF-x是編碼實現(xiàn)方式,涉及到實際傳輸,所以需要考慮字節(jié)序問題。
字節(jié)序(Byte Order Mark,BOM): 用于表示字節(jié)傳輸過程中的存儲方式,常見的實現(xiàn)方式及對應(yīng)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。剩下的沒有提及的二進(jìn)制位,全部為這個符號的unicode碼。

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

Unicode符號范圍(十六進(jìn)制) UTF-8編碼方式(二進(jìn)制)
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 字符編碼的應(yīng)用

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

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

4.1 代碼頁

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

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

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

Microsoft為了適應(yīng)世界各地的文化背景和使用習(xí)慣,在Winodows系統(tǒng)中設(shè)計了區(qū)域設(shè)置的功能。可以通過控制面板->區(qū)域與語言選項進(jìn)行系統(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進(jìn)行設(shè)置,如下圖所示:

VS中字符集設(shè)置

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

C++的新標(biāo)準(zhǔn)中引入了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)換等。用的比較多的應(yīng)該就是字符串轉(zhuǎn)換的API了,如下所示:

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

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

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

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

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

  1. 作用域問題:CA2T和CT2A的轉(zhuǎn)換后的數(shù)據(jù)作用域只在當(dāng)前行,即在下一行代碼中再去訪問轉(zhuǎn)換后的數(shù)據(jù)會出現(xiàn)不可預(yù)知的問題。如下代碼所示:
// 正確,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對應(yīng)參數(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
}
  • 從上述的代碼邏輯中可以看出,當(dāng)沒有指定轉(zhuǎn)換代碼頁的時候默認(rèn)通過_AtlGetConversionACP函數(shù)來獲取轉(zhuǎn)換參數(shù)。
  • 注意區(qū)別CP_ACP和CP_THREAD_ACP,大多數(shù)情況下這兩者對應(yīng)的代碼頁是一樣的,都是系統(tǒng)當(dāng)前的代碼頁,但是如果程序在運行時指定了其它的代碼頁則會出現(xiàn)不一致的情況。
  • CP_ACP代表的是系統(tǒng)當(dāng)前的代碼頁,但是不同系統(tǒng)中當(dāng)前的代碼頁可能是不一樣的,例如A電腦設(shè)置的是936(簡體中文),B電腦設(shè)置的是950(繁體中文),此時將一個簡體中文字符串進(jìn)行轉(zhuǎn)換時在A電腦上可以成功,但是在B電腦上就出現(xiàn)了亂碼情況。因此,建議采用CP_UTF8參數(shù)對字符串進(jìn)行轉(zhuǎn)換,降低出現(xiàn)亂碼的概率。

更多詳細(xì)內(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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容