做一家網(wǎng)站費(fèi)用平臺(tái)優(yōu)化
文章目錄
- 前言
- 一、字符集和編碼方式
- 二、unicode字符集
- utf32編碼
- utf8編碼
- utf8編碼函數(shù)示例
- utf8解碼函數(shù)示例
- utf16編碼
- utf16編碼解碼函數(shù)示例
- 總結(jié)
前言
本文詳細(xì)介紹 u n i c o d e unicode unicode 字符集和其相關(guān)的三種編碼方式: u t f 8 utf8 utf8, u t f 16 utf16 utf16 和 u t f 32 utf32 utf32,并給出一個(gè)編碼和解碼的參考程序。
一、字符集和編碼方式
字符集是一些字符的集合,字符集中每一個(gè)字符有一個(gè)唯一的字符編碼表示該字符,編碼方式規(guī)定了計(jì)算機(jī)存儲(chǔ)該字符集中字符編碼的規(guī)則,也是計(jì)算機(jī)解讀一串二進(jìn)制序列的規(guī)則。
1: A S C I I ASCII ASCII 碼用 7 b i t ( 0 x 00 ? 0 x 7 f ) 7bit \ (0x00-0x7f) 7bit?(0x00?0x7f) 存儲(chǔ)英文字符,字符集為 128 128 128 個(gè)英文字符,即 A S C I I ASCII ASCII 字符集。 A S C I I ASCII ASCII 碼的編碼方式類似直接映射,字母 A A A 對(duì)應(yīng)的字符編碼是 65 65 65,在 A S C I I ASCII ASCII 編碼下為 0 x 41 0x41 0x41。字符編碼的值也是 A S C I I ASCII ASCII 碼值。
2: A S C I I ASCII ASCII 字符集的缺陷非常直觀:只包含英文字符。
3: U n i c o d e Unicode Unicode 是國際標(biāo)準(zhǔn)字符集,它將世界各種語言的每個(gè)字符定義一個(gè)唯一的字符編碼,以滿足跨語言、跨平臺(tái)的文本信息轉(zhuǎn)換。 2023 2023 2023 年 9 9 9 月發(fā)表的 15.1 15.1 15.1 版本中定義了 149813 149813 149813 個(gè)字符。規(guī)定 U n i c o d e Unicode Unicode 字符編碼存儲(chǔ)方式的規(guī)則主要有三種: u t f 8 utf8 utf8, u t f 16 utf16 utf16, u t f 32 utf32 utf32
二、unicode字符集
U n i c o d e Unicode Unicode 為每一個(gè)字符分配一個(gè)唯一的字符編碼,稱為在編碼空間中的一個(gè)碼點(diǎn) ( c o d e p o i n t ) (code \ point) (code?point), U n i c o d e Unicode Unicode 標(biāo)準(zhǔn)給定編碼空間為 U+0000 - U+10FFFF
。碼點(diǎn)以 U + U+ U+ 開頭,最少用 4 4 4 個(gè)十六進(jìn)制數(shù)表示,若有前導(dǎo) 0 0 0 不可省略。例如: U + 00 F 7 U+00F7 U+00F7 表示除法符號(hào) ÷ ÷ ÷ 。
編碼空間中有效碼點(diǎn)個(gè)數(shù)為: 2 20 + ( 2 16 ? 2 11 ) = 1112064 2^{20} + (2^{16} ? 2^{11}) = 1112064 220+(216?211)=1112064。其減掉的 2 11 2^{11} 211 主要原因在于 u t f 16 utf16 utf16 編碼的編碼方式限制,在范圍 U+D800 - U+DFFF
內(nèi) U n i c o d e Unicode Unicode 并未編碼字符。
下表隨機(jī)列了幾個(gè) U n i c o d e Unicode Unicode 碼點(diǎn)和其表示的字符之間的對(duì)應(yīng)關(guān)系:
碼點(diǎn) | Value |
---|---|
U+2118 | P \huge\mathscr{P} P |
U+A015 | ? |
U+FE18 | ? |
另外,按照碼點(diǎn)范圍區(qū)分了不同平面,以下為具體平面名稱:
碼點(diǎn)范圍 | 平面 |
---|---|
U+0000-U+FFFF | 基本多文種平面 |
U+10000-U+1FFFF | 多文種補(bǔ)充平面 |
U+20000-U+2FFFF | 表意文字補(bǔ)充平面 |
U+30000-U+DFFFF | 表意文字第三平面 |
U+E0000-U+EFFFF | 特別用途補(bǔ)充平面 |
U+F0000-U+FFFFF | 保留作為私人使用區(qū)域A區(qū) |
U+100000-U+10FFFF | 保留作為私人使用區(qū)域B區(qū) |
基本多文種平面包含了絕大部分常用字符,例如: U + 0980 ? U + 09 F F U+0980-U+09FF U+0980?U+09FF 為孟加拉文, U + 25 A 0 ? U + 25 F F U+25A0-U+25FF U+25A0?U+25FF 為幾何圖形, U + 1800 ? U + 18 A F U+1800-U+18AF U+1800?U+18AF 為蒙古文,等等。具體見:Unicode符號(hào)表
utf32編碼
u t f 32 utf32 utf32 編碼方式非常簡(jiǎn)單直觀:用 32 b i t 32bit 32bit 直接表示一個(gè) U n i c o d e Unicode Unicode 碼點(diǎn),因此其也被稱為定長編碼。
1 1 1: U n i c o d e Unicode Unicode 標(biāo)準(zhǔn)規(guī)定的編碼空間: U+0000 - U+10FFFF
。最長需要 3 3 3 個(gè)字節(jié)表示, 4 4 4 字節(jié)完全夠用。
2 2 2:以碼點(diǎn) U + 0041 U+0041 U+0041 字符 A A A 為例,其 u t f 32 utf32 utf32 編碼結(jié)果為: 0 x 00000041 0x00000041 0x00000041。直觀來講, u t f 32 utf32 utf32 編碼方式相當(dāng)于把碼點(diǎn)零擴(kuò)展到 32 b i t 32bit 32bit。類似的, A S C I I ASCII ASCII 碼也是一樣的,零擴(kuò)展到 7 b i t 7bit 7bit 表示。
缺點(diǎn):
1 1 1: u t f 32 utf32 utf32 編碼最大的缺點(diǎn)在于占用空間過大。假設(shè)一個(gè)文件內(nèi)容只包含 A S C I I ASCII ASCII 字符集中的字符,那么用 u t f 8 utf8 utf8 來存儲(chǔ)所需的空間是用 u t f 32 utf32 utf32 來存儲(chǔ)的 1 / 4 1/4 1/4。
2 2 2: u t f 32 utf32 utf32 不兼容 A S C I I ASCII ASCII 碼。即:同樣一個(gè)十六進(jìn)制表示 0 x 41 0x41 0x41,在 A S C I I ASCII ASCII 和 u t f 8 utf8 utf8 兩種編碼中表示內(nèi)容一樣且都為字符 A A A 的合法編碼。
utf8編碼
u t f 8 utf8 utf8 編碼和 u t f 16 utf16 utf16 都為變長編碼。 u t f 8 utf8 utf8 用 1 ? 4 1-4 1?4 字節(jié)來表示一個(gè)特定字符。具體編碼規(guī)則如下所示:
碼點(diǎn)范圍 | 碼點(diǎn)二進(jìn)制表示 | 編碼規(guī)則 | 字節(jié)數(shù) |
---|---|---|---|
U + 0000 ? U + 007 F U+0000-U+007F U+0000?U+007F | 0 b x x x x x x x 0bxxxxxxx 0bxxxxxxx | 0 b 0 x x x x x x x 0b0xxxxxxx 0b0xxxxxxx | 1字節(jié) |
U + 0080 ? U + 07 F F U+0080-U+07FF U+0080?U+07FF | 0 b x x x x x x x x x x x 0bxxx \ xxxx\ xxxx 0bxxx?xxxx?xxxx | 0 b 110 x x x x x 10 x x x x x x 0b110xxxxx \ 10xxxxxx 0b110xxxxx?10xxxxxx | 2字節(jié) |
U + 0800 ? U + F F F F U+0800-U+FFFF U+0800?U+FFFF | 0 b x x x x x x x x x x x x x x x x 0bxxxx\ xxxx \ xxxx \ xxxx 0bxxxx?xxxx?xxxx?xxxx | 0 b 1110 x x x x 10 x x x x x x 10 x x x x x x 0b1110xxxx \ 10xxxxxx \ 10xxxxxx 0b1110xxxx?10xxxxxx?10xxxxxx | 3字節(jié) |
U + 01 0000 ? U + 10 F F F F U+01 \ 0000-U+10 \ FFFF U+01?0000?U+10?FFFF | 0 b x x x x x x x x x x x x x x x x x x x x x 0bx \ xxxx \ xxxx \ xxxx \ xxxx\ xxxx 0bx?xxxx?xxxx?xxxx?xxxx?xxxx | 0 b 11110 x x x 10 x x x x x x 10 x x x x x x 10 x x x x x x 0b11110xxx \ 10xxxxxx \ 10xxxxxx \ 10xxxxxx 0b11110xxx?10xxxxxx?10xxxxxx?10xxxxxx | 4字節(jié) |
1 1 1:編碼時(shí)只需根據(jù)碼點(diǎn)范圍按照碼點(diǎn)二進(jìn)制表示,高位補(bǔ) 0 0 0,填充編碼規(guī)則中所需的空缺即可。
2 2 2:解碼時(shí)只需要考慮首字節(jié)中最高位 0 b i t 0bit 0bit 左側(cè) 1 b i t 1bit 1bit 的個(gè)數(shù),即為當(dāng)前字符所占用字節(jié)數(shù)。
例如:編碼 U + 00 E 9 U+00E9 U+00E9 對(duì)于字符為 e ˊ é eˊ。根據(jù)范圍知道其需要二字節(jié)編碼, 0 0 0 擴(kuò)展到 11 b i t 11bit 11bit 為 0 b 000 1110 1001 0b000 \ 1110 \ 1001 0b000?1110?1001。依次填充可知該字符 u t f 8 utf8 utf8 編碼結(jié)果為 0 b 11000011 10101001 = 0 x c 3 a 9 0b11000011 \ 10101001=0xc3a9 0b11000011?10101001=0xc3a9。
utf8編碼函數(shù)示例
下面給出編碼函數(shù)如下所示:
參數(shù)buf為待填入編碼值的緩沖區(qū),c為32位unicode碼點(diǎn)
返回值為該字符所需編碼長度
int encode_utf8(char *buf, uint32_t c) {// 一字節(jié)編碼if (c <= 0x7F) {buf[0] = c;return 1;}// 二字節(jié)編碼,首字節(jié)待填充5位,第二字節(jié)待填充6位if (c <= 0x7FF) {buf[0] = 0b11000000 | (c >> 6);buf[1] = 0b10000000 | (c & 0b00111111);return 2;}// 三字節(jié)編碼,首字節(jié)待填充4位,第二字節(jié)待填充6位,第三字節(jié)待填充6位if (c <= 0xFFFF) {buf[0] = 0b11100000 | (c >> 12);buf[1] = 0b10000000 | ((c >> 6) & 0b00111111);buf[2] = 0b10000000 | (c & 0b00111111);return 3;}// 四字節(jié)編碼,首字節(jié)待填充3位,第二字節(jié)待填充6位,第三字節(jié)待填充6位,第四字節(jié)待填充6位buf[0] = 0b11110000 | (c >> 18);buf[1] = 0b10000000 | ((c >> 12) & 0b00111111);buf[2] = 0b10000000 | ((c >> 6) & 0b00111111);buf[3] = 0b10000000 | (c & 0b00111111);return 4;
}
可通過如下主函數(shù)測(cè)試該編碼函數(shù)的正確性:
int main(){char buf[4];int len=encode_utf8(buf,0x000000E9);system("chcp 65001"); // 終端使用utf8編碼for(int i=0;i<len;i++)printf("%x",(unsigned char)buf[i]);std::cout<<std::endl;std::cout<<buf<<std::endl;return 0;
}
用 v s c o d e + m i n g w vscode+mingw vscode+mingw 的環(huán)境下有輸出如下所示:
utf8解碼函數(shù)示例
下面給出解碼函數(shù)如下所示:
參數(shù)buf為給定utf8編碼序列
返回值為該字符unicode碼點(diǎn)
uint32_t decode_utf8(char *p) {// 單字節(jié)編碼if ((unsigned char)*p < 128) {return *p;}int len;uint32_t c;if ((unsigned char)*p >= 0b11110000) { // 四字節(jié)編碼,起始11110xxx,3bit有效len = 4;c = *p & 0b111;} else if ((unsigned char)*p >= 0b11100000) { // 三字節(jié)編碼,起始1110xxxx,4bit有效len = 3;c = *p & 0b1111;} else if ((unsigned char)*p >= 0b11000000) { // 二字節(jié)編碼,起始110xxxxx,5bit有效len = 2;c = *p & 0b11111;} else {std::cout<<"invalid UTF-8 sequence"<<std::endl;}for (int i = 1; i < len; i++) {if ((unsigned char)p[i] >> 6 != 0b10)std::cout<<"invalid UTF-8 sequence"<<std::endl;c = (c << 6) | (p[i] & 0b111111);}return c;
}
可通過如下主函數(shù)測(cè)試該編碼函數(shù)的正確性:
int main(){unsigned char buf[4]={0xc3,0xa9,0x00,0x00};uint32_t code=decode_utf8((char*)buf);system("chcp 65001");std::cout<<buf<<std::endl;std::cout<<std::hex<<code<<std::endl;return 0;
}
用 v s c o d e + m i n g w vscode+mingw vscode+mingw 的環(huán)境下有輸出如下所示:
utf16編碼
u t f 16 utf16 utf16 為變長編碼,采用 2 2 2 字節(jié)或 4 4 4 字節(jié)編碼。不兼容 A S C I I ASCII ASCII 碼。
上文提到,碼點(diǎn)范圍從 U + 0000 U+0000 U+0000 到 U + F F F F U+FFFF U+FFFF 為基本多文種平面,包括絕大多數(shù)常用字符。 u t f 16 utf16 utf16 編碼對(duì)常用的基本多文種平面直接使用 2 2 2 字節(jié)編碼,超過這個(gè)范圍的碼點(diǎn)使用 4 4 4 字節(jié)編碼。
具體編碼規(guī)則如下所示:
碼點(diǎn)范圍 | 碼點(diǎn)二進(jìn)制表示 | 編碼規(guī)則 | 字節(jié)數(shù) |
---|---|---|---|
U + 0000 ? U + F F F F U+0000-U+FFFF U+0000?U+FFFF | 0 b x x x x x x x x x x x x x x x x 0bxxxxxxxx \ xxxxxxxx 0bxxxxxxxx?xxxxxxxx | 0 b x x x x x x x x x x x x x x x x 0bxxxxxxxx \ xxxxxxxx 0bxxxxxxxx?xxxxxxxx | 2字節(jié) |
U + F F F F ? U + 10 F F F F U+FFFF-U+10FFFF U+FFFF?U+10FFFF | c o d e p o i n t ? 0 x 10000 = 0 b y y y y y y y y y y x x x x x x x x x x code \ point - 0x10000=0byyyy \ yyyy \ yyxx \ xxxx \ xxxx code?point?0x10000=0byyyy?yyyy?yyxx?xxxx?xxxx | 0 x D 800 + 0 b y y y y y y y y y y 0xD800+0byyyy \ yyyy \ yy 0xD800+0byyyy?yyyy?yy 0 x D C 00 + 0 b x x x x x x x x x x 0xDC00+0bxx \ xxxx \ xxxx 0xDC00+0bxx?xxxx?xxxx | 4字節(jié) |
1 1 1:這里四字節(jié)編碼中碼點(diǎn)需要減去 0 x 10000 0x10000 0x10000 ,最大碼點(diǎn) 0 x 10 F F F F ? 0 x 10000 = 0 x F F F F F 0x10FFFF-0x10000=0xFFFFF 0x10FFFF?0x10000=0xFFFFF。
2 2 2:上文提及 u t f 16 utf16 utf16 編碼特性使得 U n i c o d e Unicode Unicode 標(biāo)準(zhǔn)中有 2 11 2^{11} 211 個(gè)碼點(diǎn)未編碼實(shí)際字符,該未編碼字符的碼點(diǎn)范圍為: U + D 800 U+D800 U+D800 到 U + D F F F U+DFFF U+DFFF。用來作為 u t f 16 utf16 utf16 四字節(jié)編碼的范圍。
utf16編碼解碼函數(shù)示例
下面給出編碼函數(shù)如下所示:
參數(shù)buf為待填入編碼值的緩沖區(qū),緩沖區(qū)單元為2字節(jié)單元,c為32位unicode碼點(diǎn)
返回值為該字符所需編碼長度
int encode_utf16(uint16_t *buf, uint32_t c) {int len=0;if (c < 0x10000) {// 2字節(jié)編碼buf[len++] = c;return 2;} else {// 4字節(jié)編碼c -= 0x10000;buf[len++] = 0xd800 + ((c >> 10) & 0x3ff);buf[len++] = 0xdc00 + (c & 0x3ff);return 4;}
}
下面給出解碼函數(shù)如下所示:
參數(shù)buf為填入編碼值的緩沖區(qū),緩沖區(qū)單元為2字節(jié)單元
返回值為該字符的unicode碼點(diǎn)
uint32_t decode_utf16(uint16_t *buf) {uint32_t code;if ((*buf) >= 0xD800 && (*buf) <= 0xDBFF) {code = ((*buf)-0xD800)&0x3ff;buf++;if (!(*buf) >= 0xDC00 && (*buf) <= 0xDFFF){std::cerr<<"error utf16 code"<<std::endl;return 0;}code = (code<<10)|(((*buf)-0xDC00)&0x3ff);return code+0x10000;} else {return *buf;}
}
可通過如下主函數(shù)測(cè)試該編碼解碼函數(shù)的正確性:
int main(){uint16_t buf[2];int len=encode_utf16(buf,0x10ABC);for(int i=0;i<len/2;i++)printf("%x",buf[i]);printf("\n");uint32_t code=decode_utf16(buf);printf("0x%08x",code);printf("\n");return 0;
}
v s c o d e + m i n g w vscode+mingw vscode+mingw 輸出如下圖所示:
注:關(guān)于 u t f 32 utf32 utf32 編碼到 U n i c o d e Unicode Unicode 碼點(diǎn)的轉(zhuǎn)換則不需要程式,直接通過無符號(hào)擴(kuò)展到 32 b i t 32bit 32bit 即可,不再給出。
總結(jié)
完結(jié)撒花!