70. SequenceInputStream(文件合并)
Posted 江小白谢
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了70. SequenceInputStream(文件合并)相关的知识,希望对你有一定的参考价值。
缓冲输入字节流:
----------------------| InputStream 输入字节流的基类
----------------| FileInputStream 读取文件的输入字节流
----------------| BufferedInputStream 缓冲输入字节流 作用:提高读取文件的效率
缓冲输出字节流:
----------------------| OutputStream 输出字节流的基类
----------------| FileOutputStream 写入文件的输入字节流
----------------| BufferedOutputStream 缓冲输出字节流 作用:提高我们写入数据的效率
输入字符流:
--------------| Reader 输入字符流的基类。 抽象类
----------| FileReader 读取文件的输入字符流
----------| BufferedReader 缓存输入字符流(提高效率和扩展了FileReader的功能)。内部其实也维护了一个字符数组
扩展功能:
readLine() 一次读取文本的一行数据,如果读取到了文件末尾返回null
输出字符流:
--------------| Write 输出字符流的基类。 抽象类
----------| FileWrite 向文件输入数据
----------| BufferedWrite 缓存输出字符流。 内部维护了一个字符数组,当我们使用write的时候是把数据存储到了字符数组中,并不是写入了文件中
当我们使用flush,close方法或者数组满了的时候,才会写入文件中
扩展功能:
newLine() 添加一个回车符,实际上就是输出(/r/n)
//需求:把a.txt 和 b.txt的文本合并成一个文本
public class Demo1 { public static void main(String[] args) throws IOException { //找到目标文件 File inputfile1 = new File("D:\\新建文件夹\\a.txt"); File inputfile2 = new File("D:\\新建文件夹\\b.txt"); File outputfile = new File("D:\\新建文件夹\\c.txt"); //建立数据通道 FileInputStream fileInputStream1 = new FileInputStream(inputfile1); FileInputStream fileInputStream2 = new FileInputStream(inputfile2); FileOutputStream fileOutputStream = new FileOutputStream(outputfile); //我们建立一个集合来存储被合并文本的数据通道对象 ArrayList<FileInputStream> arrayList = new ArrayList<FileInputStream>(); arrayList.add(fileInputStream1); arrayList.add(fileInputStream2); //创建缓存字节数组 byte[] buf = new byte[1024]; //定义变量用来存储每次读取到数组中的字符长度 int length = 0; for (FileInputStream fileInputStream : arrayList) { while((length = fileInputStream.read(buf))!= -1) { //把读取的数据写入c.txt文本中, fileOutputStream.write(buf,0,length); } } //先开后关原则 fileOutputStream.close(); fileInputStream2.close(); fileInputStream1.close(); } }
我们可以发现,用上面的方式很不方便,书写很麻烦很繁琐
sun公司给我们提供了一个SequenceInputStream类:
表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,
接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止
其实我们可以发现,它的底层也是存在一个有序集合,原理一样都是遍历有序集合
SequenceInputStream的构造方法:
SequenceInputStream(InputStream s1, InputStream s2)
通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),以提供从此 SequenceInputStream 读取的字节
SequenceInputStream(Enumeration<? extends InputStream> e)
通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数。
SequenceInputStream的常用方法:
close() 关闭此输入流并释放与此流关联的所有系统资源。
read() 从此输入流中读取下一个数据字节。
read(byte[] b, int off, int len) 将最多 len 个数据字节从此输入流读入 byte 数组。
//使用第一个构造方法实现2个文件合并SequenceInputStream(InputStream s1, InputStream s2)
public class Demo2 { public static void main(String[] args) throws IOException { //找到目标文件的父路径 File inputfile1 = new File("D:\\新建文件夹\\a.txt"); File inputfile2 = new File("D:\\新建文件夹\\b.txt"); File outputfile = new File("D:\\新建文件夹\\c.txt"); //建立数据通道 FileInputStream fileInputStream1 = new FileInputStream(inputfile1); FileInputStream fileInputStream2 = new FileInputStream(inputfile2); FileOutputStream fileOutputStream = new FileOutputStream(outputfile); SequenceInputStream sequenceInputStream = new SequenceInputStream(fileInputStream1, fileInputStream2); //创建一个缓存字节数组 byte[] buf = new byte[1024]; int length = 0; while((length = sequenceInputStream.read(buf))!= -1) { fileOutputStream.write(buf, 0, length); } //先开后关原则 fileOutputStream.close(); sequenceInputStream.close(); } }
那么如果我们有很多个文件要合并呢?
第二个构造方法:SequenceInputStream(Enumeration<? extends InputStream> e)
此构造方法要接受一个Vector集合的迭代器
有序集合的体系:
-----------------| List 如果实现了List接口的集合类,具备的特点是:有序,可重复
-------------| ArrayList ArrayList底层维护的是一个Object类型的数组,特点是:查询快,增删慢
-------------| LinkList LinkedList底层使用了链表数据结构实现的。特点是:查询慢,增删快
-------------| Vector 底层也是维护了一个Object类型的数组,实现与ArrayList一样,但是Vector线程安全,操作效率低
Vector中的方法:
elements() 返回一个Enumeration<E>类型的迭代器
Enumeration接口中的方法:
hasMoreElements() 判断是否还有下一个元素,有返回true,没有返回false
nextElement() 返回下一个元素
//使用第二个构造方法实现2个文件合并SequenceInputStream(Enumeration<? extends InputStream> e)
public class Demo3 { public static void main(String[] args) throws IOException { //找到目标文件的父路径 File inputfile1 = new File("D:\\新建文件夹\\a.txt"); File inputfile2 = new File("D:\\新建文件夹\\b.txt"); File inputfile3 = new File("D:\\新建文件夹\\c.txt"); File outputfile = new File("D:\\新建文件夹\\d.txt"); //建立数据通道 FileInputStream fileInputStream1 = new FileInputStream(inputfile1); FileInputStream fileInputStream2 = new FileInputStream(inputfile2); FileInputStream fileInputStream3 = new FileInputStream(inputfile3); FileOutputStream fileOutputStream = new FileOutputStream(outputfile); //把数据通道对象放入Vector有序集合中 Vector<FileInputStream> vector = new Vector<FileInputStream>(); vector.add(fileInputStream1); vector.add(fileInputStream2); vector.add(fileInputStream3); //获取迭代器 Enumeration<FileInputStream> e = vector.elements(); //创建SequenceInputStream(Enumeration<? extends InputStream> e)对象 SequenceInputStream sequenceInputStream = new SequenceInputStream(e); //创建缓存字节数组 byte[] buf = new byte[1024]; //定义变量,用来存储每次存入数组中字节数组的数据长度 int length = 0; //读取所有需要合并的文本并放入数组中 while((length = sequenceInputStream.read(buf))!= -1) { //把数组中的文件写入d.txt文本中 fileOutputStream.write(buf, 0, length); } fileOutputStream.close(); sequenceInputStream.close(); } }
练习:
需求1:把一个MP3格式音乐切割成n份
解决:我们可以控制缓存数组的大小,用来分割。如果一个文件的大小是10MB那么我们可以把字节数组大小定义为1MB,那么每次读取的数据是1MB,
然后我们把每次读取的数据都放在不同文件中,那么就实现了切割了
需求2:把切割成n份MP3格式的音乐还原
public class Demo4 { public static void main(String[] args) throws Exception { //splitMP3(); mergeMP3(); } //需求1:把一个MP3格式音乐切割成n份(注意:被分割的音乐可以播放哦) public static void splitMP3() throws IOException { //输入的目标 File inputfile = new File("D:\\新建文件夹\\阿杜 - 撕夜.mp3"); //输出的目标文件的父目录 File outputparentfile = new File("D:\\新建文件夹"); //建立数据通道 FileInputStream fileInputStream = new FileInputStream(inputfile); //建立缓存字节数组,并定义数组大小为1MB(1024字节 = 1kb 1014kb = 1MB) byte[] buf = new byte[1024*1024]; //定义存储每次读取数据的长度的变量 int length = 0; //读取文件并进行分割 for(int i = 1 ; (length = fileInputStream.read(buf))!=-1 ; i++) { FileOutputStream fileOutputStream = new FileOutputStream(new File(outputparentfile, "part"+i+".mp3")); fileOutputStream.write(buf, 0, length); fileOutputStream.close(); } fileInputStream.close(); } //需求2:把切割成n份MP3格式的音乐还原(不按照顺序合并也可以听哦) public static void mergeMP3() throws IOException { //创建输入文件的父路径 File inputparentfile = new File("D:\\新建文件夹"); //创建Vector有序集合 Vector<FileInputStream> vector = new Vector<FileInputStream>(); //定义File类的数组并存储在父路径找到的所有文件的绝对路径 File[] files = inputparentfile.listFiles(); //遍历数组 for (File temp : files) { //筛选出后缀名为MP3的文件temp:文件的绝对路径 getName()文件的名字 endsWith()文件的后缀名 if(temp.getName().endsWith(".mp3")) { vector.add(new FileInputStream(temp)); } } //获取vector的迭代器 Enumeration<FileInputStream> e = vector.elements(); //创建输出文件的绝对路径 File outputfile = new File("D:\\新建文件夹\\合并.mp3"); //创建输出数据通道 FileOutputStream fileOutputStream = new FileOutputStream(outputfile); //创建SequenceInputStream对象 SequenceInputStream sequenceInputStream = new SequenceInputStream(e); //创建缓存数组 byte[] buf = new byte[1024]; //定义存储每次读取到数据的长度的变量 int length = 0; //边读边写 while((length = sequenceInputStream.read(buf))!= -1) { fileOutputStream.write(buf, 0, length); } } }
以上是关于70. SequenceInputStream(文件合并)的主要内容,如果未能解决你的问题,请参考以下文章
Java学习之IO流(序列流--SequenceInputStream)