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

---------------------- 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值