IO流__【转换流的字符编码】【编码与解码】【字符编码-“联通”】
常见的码表
ASCII: | 美国标准信息交换码。用一个字节的7位可以表示。 |
ISO8859-1: | 拉丁码表。欧洲码表用一个字节的8位表示。 |
GB2312: | 中国的中文编码表。 |
GBK: | 中国的中文编码表升级,融合了更多的中文文字符号。 |
Unicode: | 国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言就是unicode |
UTF-8: | 最多用三个字节来表示一个字符。 |
转换流的字符编码
示例 :
import java.io.*; class EncodeStream { public static void main(String[] args) throws IOException { // writeText(); readText(); } public static void readText() throws IOException{ InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"GBK");//转换流按照指定码表解码 char[] buf = new char[8]; int len = isr.read(buf); String s = new String(buf,0,len); System.out.println(s); isr.close(); } public static void writeText() throws IOException{ OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("UTF-8.txt"),"UTF-8");//转换流按指定码表编码 osw.write("你好"); osw.close(); } }调用writeText();方法;将转换流编码分别指定为(("GBK.txt"),gbk)和(("UTF-8.txt",UTF-8)),生成GBK和UTF-8两个txt文档;
发现同样是“你好”两个字符,UTF-8编码的占了6个字节,gbk编码的占了4个字节
调用readText();方法;
当其各自按照各自指定的码表解码时都能够读取出“你好”;当其按照非指定的码表解码时会产生乱码(左图);“你好”在GBK和UTF-8两种编码中的字节数组(右图)
分析原因图解:其各自的编解码方式不同导致了结果
-------------------------------------------------------------------------------------------------------------------------------
编码与解码:
编码:字符串变成字节数组;String -->byte[];
(String类的方法):str.getBytes(charsetName);;将该字符串按照指定编码表编码。
解码:字节数组变成字符串;byte[] -->String;
(String类的构造方法):new String(byte[],charsetName);通过使用指定的
charset 解码指定的 byte 数组。
方法空参则代表使用系统默认的编码表,中文为GBK,设定charsetName可以用指定的编码表
编码与解码示例:
import java.util.*; //Arrays类 class EncodeDemo { public static void main(String[] args) throws Exception { String s = "你好"; byte[] b1 = s.getBytes("GBK");//GBK编码 sop(Arrays.toString(b1)); String s1 = new String(b1,"ISO8859-1"); //解码,码表不对应,产生乱码 sop("s1:"+s1); byte[] b2 = s1.getBytes("ISO8859-1"); //将其解码出的乱码再次编码,字节数组 sop(Arrays.toString(b2)); String s2 = new String(b2,"GBK");//解码 sop("s2:"+s2); } public static void sop(Object obj){ System.out.println(obj); } }
运行结果:
图解:
实际应用场景:客户端与服务器端的码表不一致,例如一个服务器端的网页在客户端打开时就可能会产生乱码,所以要加上一个编码与解码的过程,避免这种情况的发生
开发中会经常遇到这种情况,要注意!
注意事项:
GBK不能用UTF-8作为中间码来编解码,如果将上文EncodeDemo中“ISO8859-1”改成“utf-8”
运行结果:
产生这种结果的原因是:UTF-8一个字符所占的字节个数比GBK(2个)要多,utf-8解码后的乱码经其再次编译后产生的字节数组与GBK编译后的字节数组不一致
要避免这种情况。
字符编码-“联通”
在空记事本上输入“联通”两个字,保存后再次打开,会发现只有两个乱码
首先要了解UTF-8修改版特点:
两个字节110开头,三个字节1110开头
通过打印其字节数组和二进制位:
import java.util.*; class EncodeDemo2 { public static void main(String[] args) throws Exception//加入编码表会抛异常 { String s = "联通"; byte[] by = s.getBytes("gbk"); //gbk编码 sop(Arrays.toString(by)); //打印字节数组 for (byte b:by ){ sop(Integer.toBinaryString(b&255));//打印字节数组的二进制位 } } public static void sop(Object obj){ System.out.println(obj); } }运行结果:
分析:通过“联通”的字节数组的二进制位发现:其刚好UTF-8两个字符的标准,所以原本是GBK编码的文件在再次打开时成了UTF-8解码,所以造成了乱码
在简体中文操作系统中默认的本地字符集编码是GBK编码,除非你在保存记事本文本文件时候选择了其他编码方式,否则用记事本录入的字符信息将使用GBK编码进行储存。巧合的是,“联通”这两个字符的GBK编码具有UTF-8编码的特征,记事本犯下的错误正是将GBK编码存放的记录有“联通”两个字符的文件误认为UTF-8编码的文件。