1. 字符集与字符编码
1.1 字符集(Charset)
- 字符集是一个系统支持的所有抽象字符的集合(即字符的集合)
- 字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等
- 字符集分类:
- ASCII字符集
- Unicode 字符集
- GB2312字符集
- ...
1.2 字符编码(Character Encoding)
字符编码就是将符号转换为计算机能识别的二进制编码
编码(Encoding),将数字映射到字符
2. UTF-8 编码
UTF-8 特点:可变长编码,使用1~4个字节表示一个符号,根据不同的符号而变化字节长度
2.1 UTF-8的编码规则
- 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的Unicode码,对于英文字母,UTF-8编码和ASCII码是相同的。
- 对于 n 字节的符号(n > 1),第一个字节的前n位都设为1,第 n+1 位设为0,后面字节的前两位一律设为
10
,剩下的二进制位就是这个符号的 Unicode 码
2.2 Unicode编码 与 UTF-8 编码对照表
UTF-8 编码把一个 Unicode 字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节
Unicode | UTF-8(二进制) |
---|---|
U+0000 – U+007F | 0xxxxxxx |
U+0080 – U+07FF | 110xxxxx 10xxxxxx |
U+0800 – U+FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
U+10000 – U+10FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
3. 字节处理
/**
* 11110xxx => 0xF0
*/
@Test
public void convert2() {
System.out.println(0xF0);
System.out.println(Integer.valueOf("F0", 16));
System.out.println(Integer.toBinaryString(Integer.valueOf("F0", 16)));
}
3.1 byte 转换为 Int
@Test
public void byteToInt() {
// 默认转换
byte b = -3;
int i = b;
// 强制转换
byte b2 = -3;
int i2 = (int) b2;
System.out.println(i2);
// 无符号转换
i = Byte.toUnsignedInt(b);
System.out.println(i); // 253
}
3.2 生僻字判断
@Test
public void check() {
printBinary("E0");
printBinary("C0");
printBinary("F0");
printBinary("F7");
printBinary("F8");
printBinary("FB");
printBinary("FC");
printBinary("FD");
System.out.println(uncommonChar("\uD844\uDEFE"));
System.out.println(contain4BytesChar("别墅\uD844\uDEFE"));
}
private void printBinary(String hex) {
Integer i = Integer.valueOf(hex, 16);
System.out.println("hex: "+ hex + "; i: " + i + "; 二进制:" +Integer.toBinaryString(i));
}
/**
* 正常汉字是三个字节,生僻字一般是 4-6 个字节
*
* 1字节 0xxxxxxx
* 2字节 110xxxxx 10xxxxxx
* 3字节 1110xxxx 10xxxxxx 10xxxxxx
* 4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
* 5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
* 6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*/
public static boolean uncommonChar(String data) {
if (StringUtils.isBlank(data)) {
return false;
}
byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
for (byte b : bytes) {
int i = Byte.toUnsignedInt(b);
return (0xf0 <= i && i <= 0xf7) // 4字节开头
|| (0xf8 <= i && i <= 0xfb) // 5字节开头
|| (0xfc <= i && i <= 0xfd);//6字节开头
}
return false;
}
/**
* 是否包含4字节字符
*/
public static boolean contain4BytesChar(String data) {
if (StringUtils.isBlank(data)) {
return false;
}
try {
byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
for (int i = 0; i < bytes.length; i++) {
byte bt = bytes[i];
if ((bt & 0xE0) == 0xC0) {
// 两个字节就跳过一个字节
i++;
} else if ((bt & 0xF0) == 0xE0) {
// 三个字节就跳过两个字节
i += 2;
} else if ((bt & 0xF8) == 0xF0) {
// 检测到四个字节
return true;
}
}
} catch (Exception e) {
}
return false;
}