黑马程序员------------------IO流

来源:互联网 发布:数据封装过程 编辑:程序博客网 时间:2024/06/05 10:26
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

计算机中的流其实是一种信息的交换.它是一种有序流.因此对于某一对象.通常我们把对象接受外界的信息输入(Input)称为输入流,相应地从对象向外输出(Output)信息称为输出流.合称为输入/输出流(I/O Streams). 流是信息传递过程中的载体.

流的分类按处理的数据类型可分为:字节流和字符流。字节流都是以Reader、Writer结尾,字符流以Stream结尾。字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 字节流和字符流的区别:读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。在字节流和字符流之间还有一个桥梁转换流,它可以把字节流转换成字符流。并且除了普通的转换(把字节流按系统默认编码转成字符流),还可以指定编码类型,这是在处理编码问题经常为用到的。按流的方向可分为:输入流和输出流。输入是Input和Reader、输出是Output和Writer。注意这里的输入和输出是相对于程序而言,不是相对于文件。基本上所有的流都包括在这个系统中,除了将输入和输出合为一体的随机文件访问流RandomAccessFile
流的使用对于输出流,IO流中些数据不会直接写到目的地,而是先写到内存中(即流中),用flush方法刷新流,就可以让数据写到目的地,另外流关闭时会自动flush。对于输入流也就是读取流,它的读取方法是一个阻塞方法,所谓阻塞方法就是它会一直等到读到值,否则程序就一直卡在那里。流一般是成对出现,但是也不一定。有时候源和目的不一样也会使用不同的流。为了提高流的效率一般在简单的流外面再套其它的流,这种装饰的方法可以在保持原有功能的时候增加效率,有一些修饰类的方法也很好用。典型的就是BufferedReader的readLine方法,一次可以读取一行数据。Buffered修饰类可以在流外面套一个缓冲区,数据会先存到缓冲区再往外写。流涉及到数据在不同介质中的传输,它与系统相关的异常也非常的多。只要和本地系统上的数据发生关系无论读写均会有IO异常。IO流的操作原则总结就是三个明确(1)明确源和目的        是源就用输入流(读取)InputStream、Reader        是目的就用输出流(写入)OutputStream、Writer(2)明确操作的数据是否是纯文本(操作数据类型)        是用字符流,虽然字节可以处理但会涉及编码问题,用字符流来解决将会变得非常简单        不是就用字节流(3)流体系明确后,要明确操作的数据对象        对象是通过数据存储设备来分的。如源设备就有:内存、硬盘、键盘(标准输入);目的设备有:内存、硬盘、控制台(标准输入)        硬盘通常是文件涉及文件读写就是File打头的流,内存可以是很多种例如存到字节数组中的流ByteArrayOutputStream,标准输入和输出的写法比较固定,输出一般是打印流System.out,它是一个PrintStream。标准输入带缓冲区的标准写法是:BufferedReader bufr = new BufferedReader(new InputStream(Syatem.in))。在三个明确之后再考虑效率的问题,需要提高效率就得用到Buffer缓冲区了。
一些特殊的流源和目的都是内存的流DataInputStream、DataOutputStream; ByteArrayInputStream、ByteArrayOutputStream; CharArrayReader、CharArrayWriter;StringReader、StringWriter
随机文件访问流RandomAccessFile,它是唯一一个既不是输出也不是输入的流(其实它不是随机的,而是内部有一个指针来控制文件读取,并且它内部封装了InputStream和OutputStream)。它的特点:(1)即能读取文件,也能写入文件。原因是它内部封装了两种流。(2)它可以指定文件的读写位置。它里面有一个方法seek(int i)指定文件的读取位置,i是指指针所处第几个字节。指针以每个字节为角标,从0开始,如:seek(10)指将指针指到第11个字节,从这个字节开始操作。另外该类还有个skipBytes方法和seek很类似,但是它只能向后不能回撤,没有seek的功能强大,所以一般还是用seek。这个特点就可以用它对文件进行同时分段操作,例如下载模式就是这种。(3)它可以指定文件的操作模式它的构造方法必须传入一个String mode,它是用字符串表示的一种模式。共有4中,其中“r”只读模式和“rw”读写模式最多见。如果是“r”只读模式的话,如:new RandomAccessFile(File f, r),创建对象时会在硬盘上查找f这个文件,如果不存在会报FileNotFoundException,这个和FileInputStream的构造方法很像。但是如果是“rw”模式的话,就与FileOutputStream不一样,它会查找是否有该文件,没有的话创建有的话不会再创建并覆盖,会直接用已经存在的文件,这个与File的creatNewFile方法一样。字符编码相关的流主要有两个转换流InputStreamReader和OutputStreamWriter,两个打印流PrintWriter和PrintStream但是打印流没有对应的输入流,只能打印(输出数据)不能写入,所以用的最多的还是两个转换流。另外还有一些流可以用到readUTF和WriterUTF方法,只对某个特定的编码(UTF-8)进行操作。打印流单独介绍打印流是因为打印流的方法非常好用,它即可保持数据的原样输出,构造方法不经可以接收字节流还可以接收字符流,并且还可以指定模式为自动刷新不用每次都写flush,还有就是随着JVM退出他也会退出,甚至可以不写close。
另外管道流,序列流也都是比较特殊的流. 下面以一个大文件的切割合并的例子,来看流的使用特点.
<pre class="java" name="code">import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.SequenceInputStream;import java.util.ArrayList;import java.util.Vector;/*目的:文件切割小程序,先把文件切成若干份,然后再合并到一起.先如今就切割一个比较大的文件(大于虚拟机内存) *思路:首先明确使用什么流,源和目的都是文件,文件不一定是文本文件所以用字节流,处理对象是文件,所以是FileInputStream *      和FileOutputStream.提高效率的话就用BufferedInputStream和BufferedOutputStream. *   其次考虑中间临时存贮变量,即buf.用buffered自带的不合适,因为无法自动指定大小,那样就无法控制将文件分成几分,所以 *  上面的还不能用自带缓冲的,还是得用FileInputStream和FileOutputStream. *  我们指定临时buf的空间不能过大造成内存溢出,将设指定1M,必须得弄一个计数器来控制存多少次,等于这个次数就换一个文件存贮 * 即换一个文件写入流FileOutputStream. * 文件存储完毕要考虑合并的问题,我们可以弄一个循环把文件读取流数组一个一个的读到一个文件(即一个文件写入流中),或者用合并流 * 即序列流来完成即可. *步骤:1.获得要切割的文件对象,获得其长度,若大于50M就用1M的buf存50次,将其按50M等分.(小于50M的部分这里就不写了,直接用1M的buf) *   2.由于不知道文件什么时候结束,就用while(buf!=-1)来判断,计数器count=50就new一个新的FileOutputStream,主要是其构造 *  函数的File不同,并且count重置为0; *   3.先考虑用读取流容器的方式完成文件合并,用一个循环new一系列文件读取流关联切割玩的一系列碎片文件,把这些流存入List容器中(必须保证取的时候有序). * */public class SplitFileDemo {public static void main(String[] args) {File f = new File("E:/Java/SplitFile/popo.rmvb");// 要切割的源文件System.out.println(f.length());split(f);File temp = new File("E:/Java/SplitFile/temp");// 文件切割完的保存位置,应该定义在与Classpath相关联的目录中更好mergeFiles2(temp);}public static void split(File f)// 切割文件{FileInputStream fis = null;FileOutputStream fos = null;byte[] buf = new byte[1024 * 1024];int count = 1;// 记录文件的大小,每50M就换一个存储int num = 1;// 文件名中的号码try {fis = new FileInputStream(f);fos = new FileOutputStream("E:/Java/SplitFile/temp/" + (num++)+ ".part");int len = 0;while ((len = fis.read(buf)) != -1) {if (count == 50) {fos = new FileOutputStream("E:/Java/SplitFile/temp/"+ (num++) + ".part");count = 0;}fos.write(buf, 0, len);count++;}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}public static void mergeFiles2(File dir)// 合并片段文件,传入的是片段文件的文件夹路径{File[] files = dir.listFiles();BufferedOutputStream bos = null;BufferedInputStream bis = null;try {bos = new BufferedOutputStream(new FileOutputStream("E:/Java/SplitFile/popo2.rmvb"));} catch (FileNotFoundException e1) {e1.printStackTrace();}Vector<BufferedInputStream> v = new Vector<BufferedInputStream>();// 读取流的集合,用来创建序列流时候用for (File f : files) {if (f.isFile())try {bis = new BufferedInputStream(new FileInputStream(f));v.add(bis);} catch (FileNotFoundException e) {e.printStackTrace();}}SequenceInputStream sis = new SequenceInputStream(v.elements());// 序列流int len = -1;try {while ((len = sis.read()) != -1) {bos.write(len);}} catch (IOException e) {e.printStackTrace();} finally {if (bos != null)// 流的关闭要先判断是否为null,并且要每个单独关闭try {bos.close();} catch (IOException e) {e.printStackTrace();}if (sis != null)try {sis.close();} catch (IOException e) {e.printStackTrace();}}}public static void mergeFiles(File dir)// 不用序列流,用普通的流来实现文件拼接方法{File[] files = dir.listFiles();BufferedOutputStream bos = null;BufferedInputStream bis = null;try {bos = new BufferedOutputStream(new FileOutputStream("E:/Java/SplitFile/popo2.rmvb"));} catch (FileNotFoundException e1) {e1.printStackTrace();}ArrayList<BufferedInputStream> l = new ArrayList<BufferedInputStream>();for (File f : files) {if (f.isFile())try {bis = new BufferedInputStream(new FileInputStream(f));l.add(bis);} catch (FileNotFoundException e) {e.printStackTrace();}}System.out.println(l);int i = -1;try {for (BufferedInputStream temp : l) {try {while ((i = temp.read()) != -1) {bos.write(i);}} catch (IOException e) {e.printStackTrace();} finally {if (temp != null)try {temp.close();} catch (IOException e) {e.printStackTrace();}}}} finally {if (bos != null)try {bos.close();} catch (IOException e) {e.printStackTrace();}}}}</pre><br><br>

---------------ASP.Net+Android+IOS开发、.Net培训、期待与您交流!---------------详细请查看:http://edu.csdn.net
0 0
原创粉丝点击