Java中的UTF-8、UTF-16编码字符所占字节数

时间:2022-01-06 21:09:46

前言:上一篇文章写了关于Unicode,以及utf-8、utf-16相关知识。所以本篇博文来验证在java环境下,字符在不同编码下所占的字计数。

测试代码如下:

package string;

public class CharByteTest {

public static void main(String[] args) throws Exception {
// 第二个字符为BMP之外的字符,csdn编辑器无法显示该字符,可以在运行结果截图中看到
String[] strArr = {"中", "��", "a", "aa"};
String[] charsetArr = {"gbk", "utf-8", "utf-16", "gb2312"};
for(String str : strArr) {
System.out.println(str);
for(String charset : charsetArr) {
byteTest(str, charset);
}
System.out.println("============================");
}
}

public static void byteTest(String str, String charset) throws Exception {
System.out.println("编码:" + charset
+ "\t所占字节数:" + str.getBytes(charset).length);
}

}

运行结果如下:

Java中的UTF-8、UTF-16编码字符所占字节数

在前一篇文章的基础上,我们来分析一下运行结果。

  1. “中”字的unicode码值为4E2D,使用UTF-8编码占3个字节。由于该字符位于BMP内,所以使用UTF-16编码应该占2个字节,但是运行结果为4个字节。
  2. 第二个字符(csdn编辑器不支持该字符的显示),该字符使用UTF-8应该占4个字节,运行结果正确。由于该字符位于BMP外,所以使用UTF-16编码应该占4个自己,但是运行结果显示占用了6个字节。
  3. 英文字母a,UTF-8编码应该和ASCII编码相同占用一个字节,运行结果显示占用一个字节。a在Unicode中位于BMP内,所以UTF-16编码应该占用4个字节,但是运行结果缺显示4个字节。
  4. 按照上面的运行结果,a在UTF-8编码下占用1个字节,在UTF-16编码下占用4个字节。那么猜测两个英文字母a,即”aa“在UTF-8和UTF-16编码下应该分别占2个和8个字节,但是运行结果却和想象中的不一样,aa在UTF-16编码下工占6个字节。

运行结果好像和上一篇中讲到的有点不相符啊!为什么会出现这样的结果的?

通过搜索相关文章,了解到java的字节码文件(.class)文件采用的是UTF-8编码,但是在java 运行时会使用UTF-16编码。在转码的时候会在前面加上表示字节顺序的字符,这个字符称为”零宽度非换行空格”(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。FEFF占用两个字节,所以就解释了为什么java环境下英文字母a在UTF-16编码占3个字节。

我们不妨将这些字符的在不同编码下的二进制转换为16进制并打印出来。
将代码修改如下:

package string;

public class CharByteTest {

private static char[] HEX_CHAR = {'0', '1', '2', '3', '4',
'5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

public static void main(String[] args) throws Exception {

String[] strArr = {"中", "��", "a", "aa"};
//String[] charsetArr = {"gbk", "utf-8", "utf-16", "gb2312"};
String[] charsetArr = {"unicode", "utf-8", "utf-16", "utf-16BE", "utf-16LE"};
for(String str : strArr) {
System.out.println(str);
for(String charset : charsetArr) {
byteTest(str, charset);
}
System.out.println("============================");
}
}

public static void byteTest(String str, String charset) throws Exception {
byte[] strByte = str.getBytes(charset);
System.out.println("编码:" + charset
+ "\t所占字节数:" + strByte.length
+ "\t16进制:" + bytesToHexStr(strByte));
}

// 将byte[]用十六进制字符串
public static String bytesToHexStr(byte[] bytes) {
int index = 0;
char[] hexChar = new char[bytes.length * 2];
for(int i = 0; i < bytes.length; i++) {
hexChar[index++] = HEX_CHAR[bytes[i] >> 4 & 0xF];
hexChar[index++] = HEX_CHAR[bytes[i] & 0xF];
}
return new String(hexChar);
}

}

运行结果:
Java中的UTF-8、UTF-16编码字符所占字节数