一、乱码问题
我们来看下列例子:
public class ConStream { //当前平台默认采用GBK public static void main(String[] args){ String str = "依风"; byte[] by = new byte[48]; byte[] bz = new byte[48]; try { by = str.getBytes("UTF-8");//设置编码方式为utf-8,即将依风以utf-8的个数转换为byte bz = str.getBytes("GBK"); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(new String(by));//解码,默认采用平台默认格式,此处为GBK,与编码格式不同会产生乱码 System.out.println(new String(bz,0,3));//编码与解码采用格式相同,但解码的字节缺少,会出现乱码 System.out.println(new String(bz));//编码解码格式相同,且字符为缺少,解码正常不会出现乱码 } }
运行结果:
渚濋� 依? 依风
eclipse采用的编码格式,在菜单栏点击Windows-->Preferences-->General-->Workspace 查看默认编码方式
可以看到上面出现乱码的主要原因有两个:
1.编码和解码格式不统一。
2.字节丢失。
而转换流主要就是解决第一个问题的,它可以在读取和写入时指定编码格式,这样统一后就可以避免乱码问题。
二、 InputStreamReader & OutputStreamWtriter
InputStreamReader是从字节流到字符流的桥梁:它读取字节并使用指定的字符集(UTF-8、GBK等)将它们解码成字符。
OutputStreamWriter是从字符流到字节流的桥梁:写入其中的字符使用指定的字符集(UTF-8、GBK等)编码为字节。
三、构造方法
OutputStreamWriter(OutputStream out, String charsetName)
InputStreamReader(InputStream in, String charsetName)
初始化传递进去的是输入输出流对象,charseName是指定的编码格式。
剩下的操作方法与输入输出流类似。
四、例子
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; public class ConStream { public static void main(String[] args){ // File f = new File("F:\依风\Desktop\UTF-8.txt"); File f = new File("F:\依风\Desktop\test.txt"); String write = "依风 依风 依风"; String read; try { char []c = new char[1024]; //字节输入输出流-->转换流-->字符缓冲流 //以一种编码格式写入 OutputStream w = new FileOutputStream(f); OutputStreamWriter ow = new OutputStreamWriter(w,"GBK");//将字符转换为GBK格式的字节码并写入 BufferedWriter buffW = new BufferedWriter(ow); buffW.write(write); buffW.flush(); buffW.close(); w.close(); //将写入的数据 InputStream r = new FileInputStream(f);//字节流 InputStreamReader ir = new InputStreamReader(r,"UTF-8");//将读入GBK格式的字节码,并用UTF-8格式转换为字符 BufferedReader buffR = new BufferedReader(ir);//字符缓冲流 while(null != (read = buffR.readLine())){ System.out.println(read); } buffR.close(); r.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
运行结果:
????
????
????
先将字符串以GBK方式编码成字节,然后将GBK格式的字节写入文件。
然后将GBK格式的字节以UTF-8格式解码成字符。
上列代码运行后显示的是乱码,因为写入和读取采用的编码方式不一样。
看个图更好理解:
可见转换流主要就两个功能:
1、字节流与字符流字节转换的桥梁
2、指定字节转换为字符的编码方式
如果把上述代码中的读、写编码格式一致,则不会出现乱码。反之则会出现乱码。
还要一点需要注意:
文件打开时采取的解码方式与程序写入时的编码方式无关。
举个例子,假如我是将字符串转换为GBK格式的字节,然后将GBK格式的字节写入记事本(*.txt)。
我们打开记事本时记事本首先要解码(将二进制的字节转换为字符),但此时采用的解码规则不一定是GBK。
可能是GBK,也可能是UTF-8。记事本采取的解码规则是记事本本身绝对的,记事本会根据本身存储的字节信息进行判断,
然后选取对应的编码,这个判断大多数情况下时准确的但也有例外。
例如在记事本中写入“连通”,然后另存为指定下编码规则为ANSI,即在windows平台是采用下GBK编码规则。
然后再次打开记事本会发现显示的是乱码。因为“连通”的GBK格式编码和UTF-8格式的编码“类型”,所以记事本会用UTF-8的方式进行解码。
采用GBK编码方式的字节码用UTF-8编码方式解码就会出现乱码问题。
输入连通
另存为,修改编码规则为ANSI(GBK)
保存退出,然后再次打开该文本。
我们再次点击另存为:
编码中显示的UTF-8就代表记事本当前的编码规则。
也就是说我们输入“连通”然后用GBK格式保存,记事本自己判断这个编码是UTF-8,然后采用UTF-8方式解码,然后我们看到的就是乱码。
当然这只是个例,换一些别的字符采用上述操作不会出现乱码。