带你读《全景揭秘字符编码》之十:常见字符编码4:UNICODE(8) https://developer.aliyun.com/article/1240879?groupCode=tech_library
10.5.2程序算法
用文字不太好描述算法结构,我们就直接来欣赏一下UTF-8鼻祖写的这段解析代码,这是Ken Thompson(B语言、C语言的作者、Unix之父)和 Rob Pike 用一个晚上写出来的编解码算法,代码非常简短精炼,为了方便阅读我加了注释解读。
typedefstruct
{
intcmask; //前缀码掩码
intcval; //前缀码
intshift; //移动位数
longlmask; //Unicode值掩码
longlval; //Unicode下限值
} Tab;
staticTab tab[] =
{
0x80, 0x00, 0*6, 0x7F, 0, /* 1 byte sequence */
0xE0, 0xC0, 1*6, 0x7FF, 0x80, /* 2 byte sequence */
0xF0, 0xE0, 2*6, 0xFFFF, 0x800, /* 3 byte sequence */
0xF8, 0xF0, 3*6, 0x1FFFFF, 0x10000, /* 4 byte sequence */
0xFC, 0xF8, 4*6, 0x3FFFFFF, 0x200000, /* 5 byte sequence */
0xFE, 0xFC, 5*6, 0x7FFFFFFF, 0x4000000, /* 6 byte sequence */
0, /* end of table */
};
/**
* 把一个多字节序列转换为一个宽字符
*
* @param p 存放计算后的unicode值
* @param s 需要解析的UTF-8字节序列
* @param n 字节长度
* @return 解析的字节长度
*/
intmbtowc(wchar_t*p, char*s, size_tn)
{
longl; intc0, c, nc; Tab *t;
if(s == 0) return0;
nc = 0;
//异常校验(可不用关注)
if(n <= nc) return-1;
//c0 此处备份一下首字节,后续需要用到前缀码
c0 = *s & 0xff;
//l 保存 Unicode 结果
l = c0;
/* 遍历tab,从单字节结构->2字节结构->..依次检查找到对应tab */
for(t=tab; t->cmask; t++) {
//字节数+1,字节数和tab结构是对应的,也就是当nc=1时 tab结构是单字节,nc=2是tab是两字节
nc++;
/* 判断前缀码跟当前的tab是否一致, 如果一致计算最终unicode值并返回*/
if((c0 & t->cmask) == t->cval) {
//通过 & Unicode有效值掩码,移除高位前缀码,得到最终unicode值
l &= t->lmask;
//异常校验
if(l < t->lval) return-1;
//保存结果并反回
*p = l;
returnnc;
}
//异常校验
if(n <= nc) return-1;
//读取下个字节;如果上面判断前缀码不一致,说明需要再读取下个字节
s++;
//计算有效位的值,目的是去除UTF-8 编码从第二个字节开始的高两位10
// 例如 s=10101111、0x80=10000000 计算结果是00101111,这样就去除了高位前缀10
c = (*s ^ 0x80) & 0xFF;
//异常校验
if(c & 0xC0) return-1;
//重新计算unicode值,根据UTF-8规则c只有低 6 位有效,所以通过移位把c填入到l的低6位
l = (l<<6) | c;
}
//返回异常
return-1;
}
带你读《全景揭秘字符编码》之十:常见字符编码4:UNICODE(10) https://developer.aliyun.com/article/1240875?groupCode=tech_library