Unicode 是「字符集」
UTF-8 是「编码规则」
字符集:为每一个「字符」分配一个K/V表「码位」。(如:key:编码 value:对应的字符)
编码规则:将「码位」转换为字节序列的规则(编码/解码规则)
UTF-8是一套以 8 位为一个编码单位的可变长编码(1到4个字节)。 (
即:某个字符(如“A”)它只有1个字节长度;
某个字符(如“中”),它就有3个字节长度,
再如Emojj表情包,它可能有4个字节长度)。
那如何判断这个字符,到底占了几个字节呢?就是需要从UTF-8的编码说起。
1. 一个字节长度的字符,字节的第一位都为0,和ASCII码完全相同;如(A):01000001
2. n(其中n>1)个字节长度的字符,第一个字节的前n位都为1,第n+1位为0,后面字节的前两位都设为10,这n个字节的其余空位填充该字符unicode码,高位用0补足。
如:“中”,对应三个字符:11100100 10111000 10101101 -->红色部分为标记位,黑色为数据位;
-->得到0100 111000 101101 ->0x4E2D ->unicode表,查询是"中"字。
如果是4个字符的编码就是:11110XXX 10XXXXXX 10XXXXXX 10XXXXXX
知道这个规则后,我们就可以用来判断某些文件编码是不是UTF-8编码。或者在mysql编码为utf-8(实际是mysql本身变异的编码,它最长只占3个字节。)时,过滤有4个字节的字符(如Emoji表情编码)。
public static String re(String nickName) {
StringBuilder sBuilder = new StringBuilder();
byte[] t1 = nickName.getBytes();
for (int i = 0; i < t1.length;) {
byte tt = t1[i];
int code = 0;
if ((tt & 0x80) == 0x0) {
// 单字节。
code = tt;
i++;
} else if ((tt & 0xE0) == 0xC0) {
// 2个字节时:编码 110XXXXX 10XXXXXX
// 0x1F->11111 ,0x3F 111111
// t1[i] & 0x1F ->取第一组的后5位,先占到第11-6位置(<<6)
// t1[i+1] & 0x3F->取第二组的后6位)
code = (((int) (t1[i] & 0x1F)) << 6) | ((int) (t1[i + 1] & 0x3F));
i += 2;
} else if ((tt & 0xF0) == 0xE0) {
// 3个字节时:编码 1110XXXX 10XXXXXX 10XXXXXX
// 0x0F->1111 ,0x3F 111111
// t1[i] & 0x0F ->取第一组的后4位,先占到第16-12位置(<<12)
// t1[i+1] & 0x3F->取第二组的后6位,占到第11到6位置(<<6)
// t1[i+2] & 0x3F->取第三组的后6位,
code = (((int) (t1[i] & 0x0F)) << 12) | (((int) (t1[i + 1] & 0x3F)) << 6) | (t1[i + 2] & 0x3F);
i += 3;
} else if ((tt & 0xF8) == 0xF0) {
// 4个字节时:编码 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX
code = (((int) (t1[i] & 0x07)) << 18) | (((int) (t1[i + 1] & 0x3F)) << 12) | (((int) (t1[i + 2] & 0x3F)) << 6) | (t1[i + 3] & 0x3F);
i += 4;
}
if(code!=0) sBuilder.append((char) code);
}
return (sBuilder.toString());
}
|