字節流InputStream和OutputStream(二)

勿以惡小而爲之,勿以善小而不爲--------------------------劉備

勸諸君,多行善事積福報,莫作惡

上一章簡單介紹了File類的使用(一),如果沒有看過,請觀看上一章

一. OutputStream

OutputStream 是字節輸出流, 用於從程序中往文件裏寫入內容。 常常使用其子類, FileOutputStream.

一.一 OutputStream 接口方法

方法名 作用
abstract void write​(int b) 寫入單個字節,寫入內容爲 b
void write​(byte[] b) 寫入整個字節數組, 寫入內容爲 字節數組
void write​(byte[] b, int off, int len) 寫入字節數組,從off 到off+len 的內容
void close() 關閉此輸出流並釋放與此流相關聯的任何系統資源。
void flush​() 刷新此輸出流並強制任何緩衝的輸出字節被寫出

一定要關閉流。

一.二 FileOutputStream 類

對於文件的字節操作,常常使用 FileOutputStream 類。

一.二.一 構造方法

一.二.一.一 方法

方法 作用
FileOutputStream​(File file) 傳入文件, 輸出時重寫文件內容
FileOutputStream​(File file, boolean append) 傳入文件,append爲true時,追加文件內容,爲false時,重寫文件內容。
FileOutputStream​(String name) 傳入文件的路徑,相對路徑和絕對路徑均可以,輸出時重寫文件內容
FileOutputStream​(String name, boolean append) 傳入文件的路徑,append爲true時,追加文件內容,爲false時,重寫文件內容

實際上,常用這兩種方式,一種是傳入文件,另外一種是傳入文件路徑。

如果傳入的文件不存在的話,那麼構造時會創建該文件。

老蝴蝶建議, 傳入文件。

一.二.一.二 演示

   @Test
    public void conTest() throws Exception{
        //有兩種,一種是傳入文件File, 一種是傳入路徑字符串

       File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
               +File.separator+"Hello3.txt");

       //傳入File,重寫文件

        OutputStream outputStream=new FileOutputStream(file);

		//傳入File, 追加文件
        // OutputStream outputStream=new FileOutputStream(file,true);

		
        String path="E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello.txt";
		//傳入路徑, 重寫文件
        OutputStream outputStream1=new FileOutputStream(path);
		//傳入路徑,追加文件
        // OutputStream outputStream1=new FileOutputStream(path,true);


    }

一.二.二 寫入和關閉方法

重寫父類的方法, 主要是 write() 方法。

有圖片

一.三 OutputStream 寫入文件

一.三.一 write(int b) 單個寫入

寫入int 類型,如果寫入的是字符串的話,需要將字符串轉換成字節數組,然後遍歷字節數組,一個個寫入。

   @Test
    public void writeContentTest() throws Exception{
        File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello3.txt");

        //這個文件不存在
        OutputStream outputStream=new FileOutputStream(file);

        //輸入單個內容
        outputStream.write(100);

        //輸入單個內容
        outputStream.write((int)'a');



        //輸入數組
        String str="Hello,My Name is TwoButterfly\r\n";


        //一次次寫入
        byte[] bytes=str.getBytes("UTF-8");

        //一個一個寫入
        for(byte b:bytes){
            //byte 類型會自動轉換成 int 類型
            outputStream.write(b);
        }

        System.out.println("寫入內容成功");
        outputStream.close();

    }

運行程序,查看文件內容

有圖片

會將100 轉換成字節, 轉換後爲 d. (97+3, 97爲小寫字母a)

會發現,當寫入字符串時,需要一個一個的寫,效率太低。 能不能直接寫入字符串呢?

一.三.二 write(byte[] bytes) 字節數組形式寫入

當寫入字符串時,因爲是字節輸出,所以是不能直接寫入字符串的, 但卻可以直接寫入字節數組。 就是將一個個字節先封裝起來,封裝成一個字節數組,然後以字節數組爲單位,進行寫入。 建議都採用寫字節數組的形式。

 @Test
    public void writeContent2Test() throws Exception{
        File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello3.txt");

        //不是追加,是重寫。
        OutputStream outputStream=new FileOutputStream(file);

        //輸入字符串

        String str="Hello,My Name is TwoButterfly";


        //將字符串轉換成字節數組
        byte[] bytes=str.getBytes("UTF-8");

        //寫入字節數組, 也可以 後面添加 0, len 形式的。 
        outputStream.write(bytes);

        System.out.println("寫入文件成功");
        outputStream.close();

    }

運行程序,查看內容

有圖片

發現,將以前的內容,先刪除,再進行寫入。 那麼,能不能保留以前的內容呢?

一.三.三 追加內容,構造方法時令append 參數爲true

在構造方法 FileOutputStream 時,令參數 append爲true 即可。

 @Test
    public void writeContent3Test() throws Exception{
        File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello3.txt");

        //令參數爲true,追加內容
        OutputStream outputStream=new FileOutputStream(file,true);

        //輸入字符串, 追加內容
        String str="Thank you";
        //將字符串轉換成字節數組
        byte[] bytes=str.getBytes("UTF-8");

        //寫入字節數組
        outputStream.write(bytes);

        System.out.println("寫入文件成功");
        outputStream.close();

    }

運行程序:

有圖片

會發現,格式非常不好,都連在一起了, 能不能換行呢?

一.三.四 換行,寫入 \r\n

換行,只需要寫入換行符就可以了。 換行符是 \r\n

   @Test
    public void writeContent4Test() throws Exception{
        File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello3.txt");

        //令參數爲true,追加內容
        OutputStream outputStream=new FileOutputStream(file,true);

        //輸入字符串, 追加內容,並且換行
        String str="\r\n I change Line"+"\r\n I change Line too";
        //將字符串轉換成字節數組
        byte[] bytes=str.getBytes("UTF-8");

        //寫入字節數組
        outputStream.write(bytes);

        System.out.println("寫入文件成功");
        outputStream.close();

    }

運行程序:

有圖片

現在寫入的都是英文字符,能不能寫入我們偉大的中國漢字呢?

一.三.五 寫入漢字

@Test
    public void writeContent5Test() throws Exception{
        File file=new File("E:"+File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello3.txt");

        //這個文件不存在
        OutputStream outputStream=new FileOutputStream(file,true);

        //輸入漢字

        String str="\r\n你好,我是兩個蝴蝶飛";


        //寫入數組
        byte[] bytes=str.getBytes("UTF-8");

        //換一種形式
        outputStream.write(bytes,0,bytes.length);
        outputStream.close();

    }

運行程序:

有圖片

正常寫入漢字。 能夠將內容寫入到文件裏面,那麼就可以將文件內容讀取出來。

二. InputStream

InputStream 是字節輸入流, 用於讀取文件中的內容。常常使用其子類 FileInputStream.

二.一 InputStream 接口方法

方法 作用
abstract int read​() 只讀取一個內容, 並且返回該內容
int read​(byte[] b) 一次讀取多個內容,並且將內容放置到 b 字節數組裏面。返回讀取的長度
int read​(byte[] b, int off, int len) 一次讀取多個內容,讀的內容是從 off 到 off+len, 然後將內容放置到b 字節數組裏面
void close​() 關閉此輸入流並釋放與流相關聯的任何系統資源。

二.二 FileInputStream

二.二.一 構造方法

二.二.一.一 方法

方法 作用
FileInputStream​(File file) 放置進去文件
FileInputStream​(String name) 放置進去文件的路徑

可以傳入文件,也可以傳入文件的路徑, 注意,文件必須要存在,否則會拋出 FileNotFoundException 異常。

老蝴蝶建議,傳入文件的形式。

二.二.一.二 演示

  @Test
    public void conTest() throws Exception{

        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello.txt");
     //傳入文件
        InputStream inputStream=new FileInputStream(file);


        String path="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello.txt";
        //傳入文件的路徑
        InputStream inputStream1=new FileInputStream(path);
    }

二.三 InputStream 讀取文件

二.三.一 read() 一個一個讀取

  @Test
    public void read1Test() throws Exception{
        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello3.txt");

        InputStream inputStream=new FileInputStream(file);

        //讀一個,需要轉換成 char 字符進行展示
       System.out.println("第一個:"+(char)inputStream.read());

        System.out.println("第二個:"+(char)inputStream.read());

        System.out.println("第三個:"+(char)inputStream.read());


        int r=-1;
        byte[] b=new byte[(int)file.length()];
        int i=0;
        //需要一個個放置到字節數組裏面 
        while((r=inputStream.read())!=-1){
            b[i]=(byte)r;
            i++;
        }
        System.out.println("讀出文件內容:"+new String(b,0,i));
 	inputStream.close();
    }

運行程序,控制檯打印輸出:

有圖片

發現,中文目前可以正常的打印出來。

注意: read() 讀取之後,無法再重新返回去讀。

讀內容是一個一個的讀,那麼能不能讀完之後,封裝到一個數組裏面呢, 這樣便於獲取?

二.三.二 read(byte[] b ) 讀取內容後封裝到數組裏面

@Test
    public void read2Test() throws Exception{
        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello3.txt");

        InputStream inputStream=new FileInputStream(file);

		//根據文件大小,設置字節數組的大小
        byte[] bytes=new byte[(int)file.length()];

		//讀取之後,封裝到數組裏面
        inputStream.read(bytes);

        System.out.println("讀出內容:"+new String(bytes));
 inputStream.close();
    }

控制檯打印輸出:

有圖片

中文也可以正常的顯示出來,換行也能顯示。

但發現有一個問題,創建字節數組時,用的是 new byte[(int)file.length()]; 也就是先統計一下文件的大小,然後根據文件大小,去構建數組,需要先查詢一下。 這樣效率會低一些。

二.三.三 read(byte[] b) 數組長度設置爲 1024

  @Test
    public void read3Test() throws Exception{
        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello3.txt");

        InputStream inputStream=new FileInputStream(file);

        //設置長度 爲1024
        byte[] bytes=new byte[1024];

        inputStream.read(bytes);
        System.out.println("讀出內容:"+new String(bytes));
 inputStream.close();
    }

運行程序:

有圖片

會發現,後面有很多很多的空格, 浪費了 bytes 數組的空間。 可以利用 read(bytes) 方法的返回值進行控制。

二.三.四 read(byte[] b) 返回值

    @Test
    public void read4Test() throws Exception{
        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello3.txt");

        InputStream inputStream=new FileInputStream(file);

        //設置長度 爲1024
        byte[] bytes=new byte[1024];

        //返回的內容,就是讀取的長度
        int len=inputStream.read(bytes);
        System.out.println("讀出內容:"+new String(bytes,0,len));
 inputStream.close();
    }

運行程序,控制檯打印輸出:

有圖片

你以爲這樣就成功了嗎? 不,還沒有。 現在我們固定長度爲 1024, 也就是可以讀取 1KB的內容, 但是如果長度超過了 1KB, bytes字節數組是放不下的,會造成文件的內容讀取不全的, 那該怎麼辦呢?

老蝴蝶可以演示一下,這種效果。 我們可以設置長度爲 10, 這個長度肯定小於文件的大小

 @Test
    public void read5Test() throws Exception{
        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello3.txt");

        InputStream inputStream=new FileInputStream(file);

        //設置長度 爲10
        byte[] bytes=new byte[10];
        //返回的內容,就是讀取的長度
        int len=inputStream.read(bytes);
        System.out.println("讀出內容:"+new String(bytes,0,len));
 inputStream.close();
    }

這個時候,運行程序:

有圖片

會發現,文件內容沒有讀全,只讀取了前10個字節。

要想全部讀完,我們可以設置循環讀取。

二.三.五 read(byte[]b ,int offset, int len) 循環讀取內容

@Test
    public void read6Test() throws Exception{
        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello3.txt");

        InputStream inputStream=new FileInputStream(file);

        StringBuilder sb=new StringBuilder();


        byte[] bytes=new byte[10];

        int len=-1;

        while((len=inputStream.read(bytes))!=-1){
            //依次讀取的內容
            String temp=new String(bytes,0,len);
            sb.append(temp);
        }
        System.out.println("輸出讀取的內容:"+sb.toString());
 inputStream.close();
    }

控制檯打印輸出:

有圖片

可以讀取全部的內容信息,但是發現,產生了中文亂碼的問題。

這是由於在讀取中文時,準確地說在讀取到 漢字’個’時, 正好將個這個詞的字節給分開了, 一半在 bytes[9], 另一半在下一次bytes數組的 的bytes[0]位置。

如果將字節數組bytes的長度擴大,如擴大到 1024, 就不會產生亂碼問題了。

有圖片

二.四 字節讀取時,中文亂碼問題

在fileSrc目錄下,新創建一個 rz.txt 文件, 填充內容爲:

你好我是兩個蝴蝶飛你好我是兩個蝴蝶飛

我們讀取這個文件,來演示中文亂碼問題。

二.四.一 字節數組長度爲奇數時

字節數組爲奇數時,很容易造成中文亂碼。

@Test
    public void read7Test() throws Exception{
        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"rz.txt");

        InputStream inputStream=new FileInputStream(file);

        StringBuilder sb=new StringBuilder();


        byte[] bytes=new byte[5];

        int len=-1;

        while((len=inputStream.read(bytes))!=-1){
            //依次讀取的內容
            String temp=new String(bytes,0,len);
            sb.append(temp);
        }
        System.out.println("輸出讀取的內容:"+sb.toString());
 		inputStream.close();
    }

運行程序:

有圖片

有很大概率,會將中文進行拆分。

這個例子時,將 byte[5] 轉換成 byte[6],換成偶數時:

有圖片

這個時候,就可能不亂碼了。

二.四.二 字節數組長度不被3整除時

上面的字節長度爲6時,不亂碼,爲10時就亂碼了。

有圖片

爲12時,就不亂碼了,爲 14時亂碼了,爲18時不亂碼了。

老蝴蝶建議,接收的字節數組的長度最好是能被3整除的偶數。 讀取時,爲了方便,常常是1024的整數被。

所以,綜合起來就是: 能同時被1024和3整除的偶數。

爲了方便,老蝴蝶還是以 1024爲主。

二.五 讀取文件內容綜合

  @Test
    public void read8Test() throws Exception{
        File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"Hello3.txt");

        InputStream inputStream=new FileInputStream(file);
//長度設置成1024
        byte[] bytes=new byte[1024];

        int len=-1;

        while((len=inputStream.read(bytes))!=-1){
            //依次讀取的內容
            System.out.println("讀取的內容爲:"+new String(bytes,0,len));
        }
 		inputStream.close();
    }

三. OutputStream和InputStream的應用: 文件複製

文件複製,實際上就是將 InputStream和 OutputStream 結合起來使用, 兩者共同使用同一個 字節數組, InputStream將讀出的內容批量寫入到字節數組裏面, OutputStream將字節數組批量寫入到文件裏面。

三.一 文件複製方法

 /**
     * 複製字節文件
     * @param src 源文件
     * @param desc 目標文件
     * @return
     */
    public static boolean copyBin(String src,String desc) throws Exception{

        //定義兩個文件
        File srcFile=new File(src);

        File descFile=new File(desc);

        if(!srcFile.exists()||!srcFile.isFile()){
            throw new RuntimeException("源文件不存在或者源文件是目錄");
        }

        //定義InputStream 和 OutputStream

        InputStream inputStream=new FileInputStream(srcFile);

        OutputStream outputStream=new FileOutputStream(descFile);

        //1M 1M的讀取
        byte[] bytes=new byte[1024];

        int len=-1;

        //將內容寫入到 bytes字節數組裏面
        while((len=inputStream.read(bytes))!=-1){
            //將bytes字節數組寫入到文件裏面
            outputStream.write(bytes,0,len);
        }

        outputStream.flush();
        //關閉流
        outputStream.close();
        inputStream.close();
        return true;

    }

三.二 測試文件複製

複製文件和複製圖片類型都可以。

 @Test
    public void copyTest(){

        //複製普通文件
       /* String src="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"rz.txt";

        String desc="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"rzcopy.txt";*/

       //複製圖片
        String src="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"129.png";

        String desc="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"129copy.png";


        try {
            CopyUtils.copyBin(src,desc);
            System.out.println("文件複製成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

運行程序,查看文件系統

有圖片

謝謝您的觀看,如果喜歡,請關注我,再次感謝 !!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章