UTF-8 是变长编码,不同字符的字节为 1 个到 4 个字节大小。
1 个字节
以字符 A
为例,其 ASCII 码为 65(二进制:0100 0001,十六进制:0x41)
。
ASCII码表示的字符个数为 128 个,即 0~127
,故只需要 7 位二进制即可。
故其 Unicode 编码为:0100 0001
,码点为:U+0041
2 个字节
以带重音字母e(é) 为例,其 Unicode 码点为:U+00E9
换成二进制位:1110 1001
对于 2 个字节的编码,第一个字节中前 3 个 bit 为 110
,表示该字符用 2 个字节存储(110是为了与后面的隔开,如果只使用 2 位 11,第 3 位如果是 1,总的前 3 位为 111,就分不清是 2 个字节还是 3 个字节了。),之后每个字节以 10
开头,用来和其他字节区分,10开头的字节称为后续字节。
故这个字符的格式为:110xxxxx 10xxxxxx
,只有 8 个 bit,但是要填 11 个字节,故补前导 0。即填的是 00011101001
,
最后的字节为:1100 0011 1010 1001
,对应的编码单元为\xC3\xA9
。
3 个字节
以中文汉字 中
为例,其 Unicode 码点为:U+4E2D
换成二进制位:0100 1110 0010 1101
对于 3 个字节的编码,第一个字节的前 4 个 bit 为 1110
,表示该字符用 3 个字节存储,之后每个字节以 10
开头。
故这个字符的格式为:1110xxxx 10xxxxxx 10xxxxxx
,有 16 个 bit ,恰好填 16 个 bit,故填的是 11100100 10111000 10101101
最后的字节为:11100100 10111000 10101101
,对应的编码单元为:\xE4\xB8\xAD
。
4 个字节
以 emoji 😀
为例,其 Unicode 码点为:U+1F600
换成二进制位:0001 1111 0110 0000 0000
对于 4 个字节的编码,第一个字节前 5 个 bit 为 11110
,表示该字符用 4 个字节存储,之后每个字节以 10
开头。
故这个字符的格式为:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
,只有 20 个 bit ,但是要填 21 个 bit,故补前导 0。故填的是 0 0001 1111 0110 0000 0000
最后的字节为:11110000 10011111 10011000 10000000
,对应的编码单元为:\xF0\x9F\x98\x80
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
// main.cpp
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
void output_char2bit(char* s) {
std::vector<unsigned char> vec;
int n = strlen(s);
printf("%s, len = %d\n", s, n);
printf("0x");
for (int i = 0; i < n; ++i) {
unsigned char* p = (unsigned char*)&s[i];
printf("%02x", *p);
vec.push_back(*p);
}
printf("\n");
if (n < 1) return ;
// 判断其是几个字节,拿首字节去做判断,从大到小判断。
std::vector<int> bit;
int len = 0;
if ((0xf0 & vec[0]) == 0xf0) {
len = 4;
} else if ((0xe0 & vec[0]) == 0xe0) {
len = 3;
} else if ((0xc0 & vec[0]) == 0xc0){
len = 2;
} else {
len = 1;
}
// 第一个字节
for (int j = (len == 1) ? len : len + 1; j < 8; ++j) {
bit.push_back(vec[0] >> (8 - j - 1) & 1);
}
// 后续字节
for (int i = 1; i < n; ++i)
for (int j = 2; j < 8; ++j)
bit.push_back(vec[i] >> (8 - j - 1) & 1);
// 补前导0
std::reverse(bit.begin(), bit.end());
while (bit.size() % 4 != 0) {
bit.push_back(0);
}
std::reverse(bit.begin(), bit.end());
// 输出 Unicode 码点
printf("U+");
for (int i = 0, lead = 1; i < bit.size(); i += 4) {
int val = 0;
for (int j = i; j < i + 4; ++j)
val = val * 2 + bit[j];
if (val != 0) {
lead = 0;
}
if (!lead) {
printf("%X", *(unsigned char*)&val);
}
}
printf("\n");
puts("------------------------------------");
}
int main()
{
char one[] = u8"A";
char two[] = u8"é";
char three[] = u8"中";
char four[] = u8"😀";
output_char2bit(one);
output_char2bit(two);
output_char2bit(three);
output_char2bit(four);
return 0;
}
|
执行 g++ main.cpp -o main && ./main
输出结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
A, len = 1
0x41
U+41
------------------------------------
é, len = 2
0xc3a9
U+E9
------------------------------------
中, len = 3
0xe4b8ad
U+4E2D
------------------------------------
😀, len = 4
0xf09f9880
U+1F600
------------------------------------
|