1. 程式人生 > >70. SequenceInputStream(文件合並)

70. SequenceInputStream(文件合並)

enc 需求 fileinput 底層 string stat close ati 創建

緩沖輸入字節流:
----------------------| 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(文件合並)