前言
在实际的工作开发中,需要对UTF-8字符串中的字符进行统计,但字符可能占用多个字节 ,所以此时统计字节个数是不满足实际功能需求
主要涉及到的知识点为:位运算、补码、Unicode字符集、UTF-8编码规则
Unicode与UTF-8的区别?
ASCII码
在计算机中每八个二进制位组成了一个字节(Byte),一个二进制有两种状态:”0” 状态 和 “1”状态,八位的字节一共可以组合出256(2的八次方)种不同的状态,计算机在最开始只在美国使用,早期人们用 8 位二进制来编码英文字母(最前面的一位是 0),将英文字母和一些常用的字符分别用连续的字节状态表示,一直编到127就可以用不同字节来存储英文的文字,于是就把这个方案叫做ANSI的ACII编码
ANSI的ACII编码只限定于表示英文的文字,对于其他国家和地区的特殊字符无法进行编码,于是各个国家就决定把字节中最前面未使用的那一个位拿来使用,一直把序号编到了最后一个状态255用来表示新的字母、符号,所以从128~255这一字符集叫做"扩展字符集"
但是由于每个国家对于"扩展字符集"赋予了不同的含义,即国家之间可能存在不同的编码方式,这样容易导致乱码的形成
Unicode
Unicode就是为了统一世界上所有字符,将这些分配了一个唯一的数字编号,这个编号范围从 0x000000 到 0x10FFFF (十六进制),有 110 多万,每个字符都有一个唯一的 Unicode 编号,这个编号一般写成 16 进制,在前面加上 U+。例如:“马”的 Unicode 是U+9A6C
Unicode只规定了符号编码范围 ,并没有指定符号的存储方式,所以符号怎么存储还是需要对应的编码规则
UTF-8
UTF(UCS Transfer Format)标准是为了解决unicode如何在网络上传输的问题,UTF-8就是每次8个位传输数据,虽然每次是使用8位进行传输,但实际是可变长字节的,就是使用的字节是可变的,这个可变取决了该字符在Unicode的编码大小,编号小的使用的字节就小,编号大的使用的字节就相应增多
UTF-8编码规则
1、对于单字节的符号,字节的第一位为0,后面的七位为Unicode码,因为对于英文字母,Unicode码和ASCII码是一致的
2、对于n字节的符号(n>1),第一个字节的前 n 位都设为 1,第 n+1 位设为 0,后面字节的前两位一律设为 10,剩下的没有提及的二进制位,全部为这个符号的 Unicode 码
“马”的 Unicode 编号是:0x9A6C,整数编号是 39532,对应第三个范围(2048 - 65535),其格式为:1110XXXX 10XXXXXX 10XXXXXX,39532 对应的二进制是 1001 1010 0110 1100,将二进制填入进入就为: 11101001 10101001 10101100
源码
int strlenUtf8(const char *s)
{
int i = 0, j = 0;
while (s[i]) {
if ((s[i] & 0xc0) != 0x80){
j++;
}
i++;
}
return j;
}
代码分析
该函数主要是针对UTF-8字符串的字符统计,所以首先得清楚UTF-8编码规则和Unicode字符集的区别,其次是&位运算
![Unicode例子](https://cdn.staticaly.com/gh/babybeekeeper/pic_Hosting@master/20220720/Unicode例子.aom7a3uc0cs.webp)
位运算
(s[i] & 0xc0) != 0x80
0xC0=0b11000000
0x80=0b10000000
& 代表 按位逻辑与
要满足该表达式则s[i]的前两位为11,即11101001 10101001 10101100 只有11101001满足该表达式
在计算机内存中,整数一致采用补码的形式存储,所以汉字符号"马"对应的三个字节分别为-23、-87、-84
总结
对UTF-8字符串中的字符进行统计主要考察的知识点是位运算、补码、Unicode字符集、UTF-8编码规则,希望通过分享的代码一起来学习,来进步,也希望有错误的地方大佬能指出,peace&love