Java判断字节流是否是 UTF8编码方法示例
作者:岑吾
Java 判断字节流是否是 UTF8 编码
遇到本来设计时使用 GBK 编码处理的地方,在实际使用过程导入了 UTF8 编码,造成了显示文本为乱码的现象,在了解 UTF8,GBK 编码和 Unicode 标准之后,编写了 Java 判断字节流是否是 UTF8 编码的程序,如果是 UTF8 编码,则转换成 GBK 编码。
编码的基础知识
Unicode 是一种标准,GBK 和 UTF8 是具体是编码格式。Java 的字符都是以 Unicode 进行存储的,占两或四个字节(看版本,且 Unicode 编码中对应关系是存在 0x00 的编码的)。Java 中的 getBytes() 方法是和平台(编码)相关的,在中文系统中返回的可能是 GBK 或 GBK2312,在英文系统中返回的可能是 ISO-8859-1。
- Unicode 标准:是计算机科学领域里的一项业界标准,包括字符集、编码方案等,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。
- GBK 编码:汉字内码扩展规范,国标,汉字占两个字节。
- UTF8 编码:针对 Unicode 的可变长度字符编码,用 1 到 6 个字节编码 Unicode 字符,汉字一般占 3 个字节。
UTF8 编码格式
如果 Unicode 字符由 2 个字节表示,则编码成 UTF8 很可能需要 3 个字节。而如果 Unicode 字符由 4 个字节表示,则编码成 UTF8 可能需要 6个字节。用 4 个或 6 个字节去编码一个 Unicode 字符可能太多了,但很少会遇到那样的 Unicode 字符。
UTF8 编码规则:如果只有一个字节则其最高二进制位为 0,如果是多字节,其第一个字节从最高位开始,连续的二进制位值为 1,1 的个数决定了其编码的字节数,其余各字节均以 10 开头。
// Unicode6.1定义范围:0~10 FFFF // 20 0000 ~ 3FF FFFF 和 400 0000 ~ 7FFF FFFF 属于 UCS-4,UTF8 现在已经弃用了这部分内容 --------------------------------------------------------------------------------- n | Unicode (十六进制) | UTF - 8 (二进制) --+-----------------------+------------------------------------------------------ 1 | 0000 0000 - 0000 007F | 0xxxxxxx 2 | 0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx 3 | 0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 4 | 0001 0000 - 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx --------------------------------------------------------------------------------- // 以下部分弃用 5 | 0020 0000 - 03FF FFFF | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 6 | 0400 0000 - 7FFF FFFF | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx ---------------------------------------------------------------------------------
Java 如何判断单个字符编码是否是 UTF8
假设当前需要判定一个 byte[] 数组内的编码是否是 UTF8 编码,这个 byte[] 是 String 通过 getBytes() 方法获取的,判断单个字符的编码步骤如下:
- 从 byte[] 数组中获取一个 byte 并将它转换成无符号类型的 int 变量 value
- 判断 value 是否是 ASCII 字符(小于 0x80)
- 判断 value 是否是无效字符(大于 0x80,小于 0xC0,参照 UTF8 编码规则)
- 确认该字符编码的是几字节 UTF8
- 确认该字符编码的除第一个字节外的字节是否满足 10xxxxxx 格式
PS:
Java getBytes() 获取的是带符号的十六进制,实际处理时需要使用无符号十六进制。
GBK 和 UTF8 中 ASCII 字符的值是一样的。
具体程序
将十六进制流中的所有编码按照单个判定的方式便利一遍,如果有不符合 UTF8 编码规则的字符出现,则该十六进制流就不是 UTF8 编码格式的字串。
public static int byteToUnsignedInt(byte data) { return data & 0xff; } public boolean isUTF8(byte[] pBuffer) { boolean IsUTF8 = true; boolean IsASCII = true; int size = pBuffer.length; int i = 0; while (i < size) { int value = byteToUnsignedInt(pBuffer[i]); if (value < 0x80) { // (10000000): 值小于 0x80 的为 ASCII 字符 if (i >= size - 1) { if (IsASCII) { // 假设纯 ASCII 字符不是 UTF 格式 IsUTF8 = false; } break; } i++; } else if (value < 0xC0) { // (11000000): 值介于 0x80 与 0xC0 之间的为无效 UTF-8 字符 IsASCII = false; IsUTF8 = false; break; } else if (value < 0xE0) { // (11100000): 此范围内为 2 字节 UTF-8 字符 IsASCII = false; if (i >= size - 1) { break; } int value1 = byteToUnsignedInt(pBuffer[i + 1]); if ((value1 & (0xC0)) != 0x80) { IsUTF8 = false; break; } i += 2; } else if (value < 0xF0) { IsASCII = false; // (11110000): 此范围内为 3 字节 UTF-8 字符 if (i >= size - 2) { break; } int value1 = byteToUnsignedInt(pBuffer[i + 1]); int value2 = byteToUnsignedInt(pBuffer[i + 2]); if ((value1 & (0xC0)) != 0x80 || (value2 & (0xC0)) != 0x80) { IsUTF8 = false; break; } i += 3; } else if (value < 0xF8) { IsASCII = false; // (11111000): 此范围内为 4 字节 UTF-8 字符 if (i >= size - 3) { break; } int value1 = byteToUnsignedInt(pBuffer[i + 1]); int value2 = byteToUnsignedInt(pBuffer[i + 2]); int value3 = byteToUnsignedInt(pBuffer[i + 3]); if ((value1 & (0xC0)) != 0x80 || (value2 & (0xC0)) != 0x80 || (value3 & (0xC0)) != 0x80) { IsUTF8 = false; break; } i += 3; } else { IsUTF8 = false; IsASCII = false; break; } } return IsUTF8; }
UTF8 编码转 GBK 编码
// Unicode String unicodeString = "张三"; // 获取 UTF8 编码 byte[] nameUTF8 = unicodeString.getBytes("utf-8"); // UTF8 编码转 str String str = new String(name, "utf-8"); // 获取 GBK 编码 byte[] nameGBK = str.getBytes("gbk");
以上就是Java判断字节流是否是 UTF8编码方法示例的详细内容,更多关于Java字节流UTF8编码判断的资料请关注脚本之家其它相关文章!