Java 字符集 编码
Java默认的字符集是Unicode(占两个字节byte,一个字节=8比特位bit)
详解:
字符集 编码
Unicode 是「字符集」;UTF-8 是「编码规则」(是使用最广的一种 Unicode 的实现方式)
字符集:为每一个字符分配一个唯一的ID(码位)
编码规则:将码位转换为字节序列的规则(用什么方式存储)
| 英文/字节 | 中文/字节 |
Utf-8(变长) | 1 | 3 |
Utf-16 | 2 | 3-4 |
GBK | 1 | 2 |
ISO8859-1 | 1 | 1 |
Unicode | 2 | 2(标点也是) |
ASCII | 1 | 2 |
Java的处理方法:
编码问题存在两个方面:JVM之内和JVM之外。
文件编译后形成class
这里Java文件的编码可能有多种多样(可以为utf-8(常用)),但Java编译器会自动将这些编码按照Java文件的编码格式正确读取后产生class文件,这里的class文件编码是Unicode编码(具体说是UTF-16编码)。也就是说完成了从UTF-8编码的文件转成与平台无关的.class文件了,把UTF-8编码方式转成了Unicode。一旦编译成.class文件,就不用在乎关于我们程序源码的什么UTF-8编码了
因此,在Java代码中定义一个字符串String s="汉字";
不管在编译前java文件使用何种编码,在编译后成class后,他们都是一样的----Unicode编码表示。
中的编码
在JVM内部,统一使用Unicode表示,当着字符从JVM内部移动到外部时(即保存为文件系统中的一个文件内容时),就进行了编码转换,使用了具体的编码方案。因此也可以说,所有的编码转换只发生在边界的地方,也就是各种输入/输出流的起作用的地方。
JVM加载class文件读取时候使用Unicode编码方式正确读取class文件,那么原来定义的String s="汉字";在内存中的表现形式是Unicode编码。
问题
在java中,一个字符等于多少字节?
或者更详细的问:在java中,一个英文字符等于多少字节?一个中文字符等于多少字节?
Java采用unicode来表示字符,java中的一个char是2个字节,一个中文或英文字符的unicode编码都占2个字节,但如果采用其他编码方式,一个字符占用的字节数则各不相同。
代码验证如下:
public static void main(String[] args) {
String str = "测";
char x = '测';
byte[] byteStr = str.getBytes();
byte[] byteChar = charToByte(x);
System.out.println("byteStr :" + byteStr.length); // byteStr :3
System.out.println("byteChar:" + byteChar.length); // byteChar:2
}
// 通过移位获取char类型的byte数组
public static byte[] charToByte(char c) {
byte[] b = new byte[2];
b[0] = (byte) ((c & 0xFF00) >> 8);
b[1] = (byte) (c & 0xFF);
return b;
}
获取系统编码
System.out.println("系统默认编码:" + System.getProperty("file.encoding")); //查询结果UTF-8
System.out.println("系统默认字符编码:" + Charset.defaultCharset()); //查询结果UTF-8
System.out.println("系统默认语言:" + System.getProperty("user.language")); //查询结果zh
getBytes()方法详解
另外解释一下上边使用的getBytes()方法
在Java中,String的getBytes()方法是得到一个操作系统默认的编码格式的字节数组。这表示在不同的操作系统下,返回的东西不一样!
1.str.getBytes(); 如果括号中不写charset,则采用的是Sytem.getProperty("file.encoding"),即当前文件的编码方式,
2.str.getBytes("charset");//指定charset,即将底层存储的Unicode码解析为charset编码格式的字节数组方式
3.String str=new String(str.getBytes("utf-8"),"gbk")); //将已经解析出来的字节数据转化为gbk编码格式的字符串,在内存中即为gbk格式的字节数组转为Unicode去交互传递
引申
问:
"a".getBytes("Unicode").length // 结果为 4
上边已经说过Unicode一个字符占两个字节,这里为什么是4字节不是2字节?
为什么Unicode个字节
使用for循环遍历得到的byte数组(还是使用字符 a ):
-2 -1 0 97
发现前边多了一个 -2 -1 ,这其实是一个字节的BOM标志。
UNICODE 是一种字符集,在 Java 中直接使用 Unicode 转码时会按照 UTF-16LE 的方式拆分,由于 UTF-16 分为 UTF-16LE 和 UTF-16BE,也就是小端序和大端序,因此在网络传过程中,无法判断是 LE 还是 BE 序的,因此需要加上一个额外的字节序 BOM 头。BOM 头的字符是一个特殊的字符,其 Unicode 编码为 U+FEFF,字符名为“ZERO WIDTH NON-BREAKING SPACE”,根据 RFC2781 3.2 节规定,开头两个字节为 FE FF 的称为Big-Endian,开头为 FF FE 的称为 Little-Endian。
关于utf-16的解释:utf-16的方式包含2种字节序,Big Endian字节序和Little Endian字节序:
UTF-16 Big Endian:FEFF (没有含义在UCS-2中),其中FEFF为标示码
UTF-16 Little Endian:FFFE (没有含义在UCS-2中),java默认选择Little Endian字节序
因此,你直接使用 Unicode 转换字节的话,也就是按 UTF-16LE 方式进行解码,会额外地加上 BOM 的两个字节 FF FE。
解决办法:
可以使用UnicodeBigUnmarked编码
"a".getBytes("UnicodeBigUnmarked").length // 结果为2