浅析java IO流和中文乱码问题

2/22/2017来源:ASP.NET技巧人气:2151

一、IO流分类 IO流在java官方提供的API中有好多类,但是总体分为两类,面向字符的输入输出流Reader、Writer,面向字节的输入输出流OutputStream、InputStream。下面具体解释一下:

首先,何谓”面向”:所谓面向字节和面向字符,就是我在使用这些流的时候,读入内存的东西是字节上保持一致还是字符上保持一致。 这里写图片描述 如果是面向字节,首先我们知道计算机内部1个字节是一串01组成的8位数字,那么把数据读入JVM内部时,二进制不能变。和数据原本的二进制一模一样。注:不要使用Windows自带的记事本编辑任何文本文件,推荐用notepad++(博主因为是win本本被记事本坑了2天找不到错误),因为windows自带的记事本用utf-8在保存文本文件时会在文件开头增加一些字符串,会出现一些不可思议的错误:

这里写图片描述 可以看到feff就是被增加上去的东西 而如果用notepad++: 这里写图片描述 所以,作为一个开发者,坚决原理WIN自带的记事本

上面讲了面向字节是什么意思,下面讲面向字符,摘自一篇文章:

而面向字符的IO是指希望系统中的文件的字符和读入内存的“字符”(注意和字节的区别)要一致。例如我们的中文版WindowsXP系统上有一 个GBK的文本文件,其中有一个“汉”字,这个字的GBK编码是0xBABA(而Unicode编号是0x6C49),当我们使用面向字符的IO把它读入内存并保存在一个char型变量中时,我希望IO系统不要傻傻的直接把0xBABA放到这个char型变量中,我甚至都不关心这个char型变量具体的二进制内容到底是多少,我只希望这个字符读进来之后仍然是“汉”这个字。

OK,以上就是面向字节和面向字符的意思

下面讲一些具体的类

2、字节流 字节流继承架构: 这里写图片描述

这里写图片描述 这里写图片描述 具体怎么用可以查看API,这里要讨论一个中文乱码问题,请看代码:

import java.io.*; public class Input { public static void main(String[] args) { // TODO 自动生成的方法存根 try(InputStream in=new FileInputStream("C:\\Users\\Hacker\\Desktop\\a.txt")) { byte[] bbuf=new byte[1024]; while(in.read(bbuf)>0) { System.out.PRintln(new String(bbuf)); } } catch ( IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } }

其中,a.txt为 这里写图片描述 运行程序,显示结果: 这里写图片描述 全是乱码,为什么? 原因就在于String这里,我们查看API: 这里写图片描述 “通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。”,这句话得意思就是字符集的解码由平台来决定,如果你的编译环境是Eclipse,那么字符解码在这里可以查看: 这里写图片描述 可以看到编码为UTF-8,而刚才a.txt编码为GBK(这个可以用Charset.defaultCharset() 来验证)。但是如果把上面的代码放到控制台来直接编译,是可以通过并且正确显示的。所以,因为String来解析byte时的编码不同,导致了乱码。作为开发人员,用Eclipse,那么读取文件时,文件保存的编码需要是utf-8,那就不会乱码 3、处理流 处理流的作用,就是包装1个节点流,比如(FileOutputStream),看代码:

FileOutputStream fos=new FileOutputStream("test.txt"); PrintStream ps=new PrintStream(fos); //然后,输出就能这么写: ps.println("啦啦啦");//文件test.txt就会写入"啦啦啦"

可见,处理流的作用,就是为了操作更加方便。而且要注意,我们常用的System.in就是一个PrintStream,但是你如果查看System源码,会发现System.in是null,它是什么时候赋值的?看这里有人回答: http://bbs.csdn.net/topics/190099509 4、转化流 2个转化流,InputStreamReader和OutputStreamWriter。在确定我要读入的是字符时,他们可以指定字节读入内存时要用到的编码。比如刚才上面GBK保存的文本,我在eclipse因为默认用utf-8来读导致乱码,现在可以自己设置需要的编码:

import java.io.*; import java.nio.charset.Charset; public class Input { public static void main(String[] args) { // TODO 自动生成的方法存根 try { //Reader in=new FileReader("C:\\Users\\Hacker\\Desktop\\c.txt"); BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("C:\\Users\\Hacker\\Desktop\\c.txt"),"GBK"));//这里指定GBK解码,就可以解决刚才上面的乱码问题 int a; String line; StringBuffer stringBuffer = new StringBuffer(); while((line=br.readLine())!=null) { stringBuffer.append(line); } System.out.println(stringBuffer); } catch ( IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } }

5、缓冲流 仔细看上面的代码,其实用到了BufferedReader这个缓冲流,他的作用就是增加一个缓冲机制,试想没有缓冲机制,不停源源不断地在读,效率肯定低。而现在增加了缓冲机制,比如当字符读了30个,然后再放进内存,这样就能提高效率

研究了4天IO流,终于有一点眉目。过程比较艰辛,看书、看代码、网上查资料。虽然总结的可能比较浅显,等日后做项目用到更加复杂的,再继续进一步深究 2017.2.18