Java中编码分析
Java 内存以 UTF-16 对字符进行存储,所以在 Java 中1个char占两个字节。通过下面的例子分析 Java 编码细节:
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "My name is 程序员";
toHex(s.toCharArray());
// 字符串 s 按照 ISO-8859-1 编码
byte[] iso8859 = s.getBytes("ISO-8859-1");
toHex(iso8859);
// 字符串 s 按照 gb2312 编码
byte[] gb2312 = s.getBytes("GB2312");
toHex(gb2312);
// 字符串 s 按照 GBK 编码
byte[] gbk = s.getBytes("GBK");
toHex(gbk);
// 字符串 s 按照 UTF-16 编码
byte[] utf16 = s.getBytes("UTF-16");
toHex(utf16);
// 字符串 s 按照 UTF-8 编码
byte[] utf8 = s.getBytes("UTF-8");
toHex(utf8);
}
字符串 “My name is 程序员”的 char 数组为:4d 79 20 6e 61 6d 65 20 69 73 20 7a0b 5e8f 5458,按照不同的编码方式转成相应的字节:
- 按照 ISO-8859-1:
1)从上图可以看出,ISO-8859-1是单字节编码。
2)“程序员”三个汉字经过iso-8859-1编码后变成“3f”,“3f”也就是“?”,因为iso-8859-1适用于西欧字符,并不认识汉字,这种现象我们称之为“黑洞”。常见中文变成“?”,可能就是错误的使用了ios-8859-1编码。按照GB2312:
按照GBK:
使用gb2312和GBK编码时,需要查码表,从码表中找到每个字符对应的字节,然后拼装成字节数组。- 按照UTF-16
1)utf-16占两个字节,单字节范围的字符高位补0,汉字变成两个字节。
2)使用utf-16编码char转成byte后,前面多出两个字节用来保存BYTE_ORDER_MARK值,用来指明是大端序还是小端序(不同的处理器对多字节的处理方式不同)。
3)从上图可以看出使用utf-16编码只是将字符的高位和地位拆分成两个字节,所以效率很高。- 按照UTF-8
1)utf-8对单字节范围的字符任然使用单字节编码,对汉字使用3字节编码。
Java编码实现过程
String.getBytes(charsetName)过程:
Java 编码时序图
从上图可以看出,通过charsetName找到Charset类,然后根据这个字符集生成CharsetEncoder对象,这个类是所有编码集的父类,这对不同的编码集,其子类中定义了具体的实现,有了CharsetEncoder对象,就可以调用encode方法进行编码。
Java中编码的应用场景
- IO读取
public void writerString2File(String filePath, String charsetName) {
try(FileOutputStream out = new FileOutputStream(filePath);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, charsetName))) {
writer.write("写字符到文件中");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void readStringFromFile(String filePath, String charsetName) {
try(FileInputStream in = new FileInputStream(filePath);
BufferedReader reader = new BufferedReader(new InputStreamReader(in, charsetName))) {
StringBuffer sb = new StringBuffer();
char[] cbuf = new char[1024];
int len = 0;
while((len = reader.read(cbuf)) != -1) {
sb.append(cbuf, 0, len);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
- 在内存中操作编码
public void test() throws UnsupportedEncodingException {
String s = "Java是门很好的编程语言";
byte[] buffer = s.getBytes("utf-8");
String news = new String(buffer, "utf-8");
System.out.println(news);
}