Java基礎之IO系列(一)

1.本文概述

本文講解IO系列有關字符流對象部分的內容。

字符流主要包含以下四個組成對象:

FileReader
FileWriter

BufferedReader
BufferedWriter


2.Writer部分

1.Writer概述

常用方法:

abstract void close()
關閉此流,但要先刷新它。即會在關閉前先調用flush方法。
abstract void flush()
刷新該流的緩衝。
void write(char[] cbuf)
寫入字符數組。
abstract void write(char[] cbuf, int off, int len)
寫入字符數組的某一部分。
void write(int c)
寫入單個字符。
void write(String str)
寫入字符串。
void write(String str, int off, int len)
寫入字符串的某一部分。

說明:Writer是一個抽象類,無法直接實例化,因此一般使用其間接子類FileWriter。

2.FileWriter

FileWriter概述

FileWriter是用來寫入字符文件的便捷類

常用構造方法:

FileWriter(File file)
根據給定的 File 對象構造一個 FileWriter 對象。
FileWriter(File file, boolean append)
根據給定的 File 對象構造一個 FileWriter 對象。如果append爲true,則將數據寫入文件末尾處,而不是覆蓋整個文件。

說明:關於參數中的File對象部分知識將在後續博文中講解,具體可參考:

FileWriter(String fileName)
根據給定的文件名構造一個 FileWriter 對象。
FileWriter(String fileName, boolean append)
根據給定的文件名以及指示是否附加寫入數據的boolean值來構造 FileWriter 對象。如果append爲true,則將數據寫入文件末尾處,而不是覆蓋整個文件。

說明:FileWriter的方法與Writer中的方法一致,並沒有特有方法,此處不再對其方法進行列舉。

向文件中寫出數據

本例中用到的構造方法:

FileWriter(String fileName)
根據給定的文件名構造一個 FileWriter 對象。

說明:在表示文件路徑時,分隔符需要使用雙反斜槓”\”

步驟:

1.創建一個FileWriter對象,該對象初始化時必須要明確被操作的文件,並且該文件會被創建到指定目錄下。若該目錄下已有同名文件,則該文件將被覆蓋。

2.調用write方法將字符串寫入流中

3.刷新流對象中的緩衝數據,將數據寫入目標文件中
說明:輸出流在關閉前可以多次調用write以及flush方法

4.關閉流
說明:該方法會在關閉流前先調用flush方法刷新緩存,調用後流就被徹底關閉了。

示例代碼:

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterDemo {

    public static void main(String[] args) {
        try {
            //實例化字符輸出流對象
            FileWriter fw=new FileWriter("G:\\test.txt");
            //向指定文件中輸出數據
            fw.write("test:");
            fw.write("這是一份測試文件");
            //刷新流緩存
            fw.flush();
            //關閉流
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

程序運行結果

1.png

3.IO異常處理方式

與輸入輸出流相關的操作都可能拋出IOException異常,這需要我們進行一些合適的異常處理。

以下爲一個標準的IOException處理示例:

示例代碼:

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterDemo {

    public static void main(String[] args) {
        //聲明字符輸出流對象並初始化
        FileWriter fw=null; 
        try {
            //實例化字符輸出流對象
            fw=new FileWriter("G:\\test.txt",true);
            //向指定文件中輸出數據
            fw.write("\r\ntest:這是在原有基礎上添加數據!");
            //刷新流緩存
            fw.flush();             

        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //當fw存在時才關閉流,防止空指針異常
            if(fw!=null)
                try {               
                    //關閉流
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

}

4.對已存在的文件進行續寫

使用FileWriter另一個構造方法:

FileWriter(String fileName, boolean append)
根據給定的文件名以及指示是否附加寫入數據的 boolean 值來構造 FileWriter 對象。

說明:當append傳入參數爲true時,表示不覆蓋已有的文件,而在已有文件的末尾續寫數據。

示例代碼:

    //部分代碼
    public static void main(String[] args) {
        //聲明字符輸出流對象並初始化
        FileWriter fw=null; 
        try {
            //實例化字符輸出流對象
            fw=new FileWriter("G:\\test.txt",true);
            //向指定文件中輸出數據
            fw.write("\r\ntest:這是在原有基礎上添加數據!");
            //刷新流緩存
            fw.flush();             

        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            //當fw存在時才關閉流,防止空指針異常
            if(fw!=null)
                try {                   
                    //關閉流
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

程序運行結果:

2.png

小技巧:如果想要實現文本換行的效果,可以使用轉義字符”\n”,但爲了使windows下的記事本識別換行操作,需要使用”\r\n”


3.Reader部分

1.Reader概述

常用方法:

abstract void close()
關閉該流並釋放與之關聯的所有資源。
int read()
讀取單個字符。如果已到達流的末尾,則返回-1
int read(char[] cbuf)
將字符讀入數組。如果已到達流的末尾,則返回-1
abstract int read(char[] cbuf, int off, int len)
將字符讀入數組的某一部分。如果已到達流的末尾,則返回-1。off是開始位置的索引,len是要讀取的最大字符數
long skip(long n)
跳過字符

int read(CharBuffer target)
試圖將字符讀入指定的字符緩衝區。如果已到達流的末尾,則返回-1 。
void reset()
重置該流。

說明:Reader是一個抽象類,無法直接實例化,因此一般使用其間接子類FileReader。

2.FileReader

FileReader概述

FileReader用來讀取字符文件的便捷類。

構造方法:

FileReader(File file)
在給定從中讀取數據的 File 的情況下創建一個新 FileReader。
FileReader(String fileName)
在給定從中讀取數據的文件名的情況下創建一個新 FileReader。

說明:FileWriter的方法與Writer中的方法一致,並沒有特有方法,此處不再對其方法進行列舉。

第一種文本文件讀取方式

通過讀取單個字節的方式獲得文件中的所有數據。

步驟:

1.創建一個文件讀取流對象,和指定名稱的文件相關聯。如果文件不存在,則會拋出FileNotFoundException

2.調用讀取流對象的read方法
fr.read();
說明:當read方法讀取到流的末尾時,該方法返回-1。利用這一特點,可以通過while循環輸出文件中的所有數據。

示例代碼:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderDemo {

    public static void main(String[] args) {
        FileReader fr=null;
        try {
            //實例化指向已存在文件的輸入流對象
            fr=new FileReader("G:\\test.txt");

            int ch=0;
            while((ch=fr.read())!=-1){
                //將獲取到的int型數據強轉爲char型輸出
                System.out.print((char)ch);
            }               
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally{
            if(fr!=null)
                try {
                    //關閉流
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

}

程序運行結果:

test:這是一份測試文件
test:這是在原有基礎上添加數據!

第二種文本文件讀取方式

通過字符數組對文件中的數據進行讀取。

步驟:

1.定義一個字符數組,用於存儲讀到的字符
2.創建一個文件輸入流對象
3.通過read(char[])方法將文件數據讀入指定數組

說明:同樣利用read方法讀取完畢返回-1的特性,可以使用while循環讀取文件中的全部數據。注意要對獲取到的數組進行有效範圍截取,因爲在循環的最後一次數據讀取中,數組不一定被填滿。此外,爲了保持較高的讀取效率,建議將用於接受數據的字符數組大小定義爲1024的整數倍。

示例代碼:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderDemo {

    public static void main(String[] args) {
        FileReader fr=null;
        try {
            //實例化指向已存在文件的輸入流對象
            fr=new FileReader("G:\\test.txt");

            //定義用於臨時存儲數據的字符數組
            char[] bufArr=new char[1024];

            //存儲讀取到的字節長度
            int len;
            while((len=fr.read(bufArr))!=-1){
                //將獲取到的char數組轉化爲字符串,注意對字符數組有效區域的截取
                String endStr=new String(bufArr,0,len);
                System.out.print(endStr);
            }               
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally{
            if(fr!=null)
                try {
                    //關閉流
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

}

程序運行結果:

test:這是一份測試文件
test:這是在原有基礎上添加數據!

說明:向控制檯中打印數據時應該使用print方法,否則可能在錯誤的位置進行換行,影響輸出效果

總結:以上兩種方法推薦使用第二種。第二種方式由於具有字符數組作爲緩衝區,相比之下讀取效率更高


3.簡單應用-複製文本文件

需求描述:將一個文本文件從E盤複製到D盤

複製的原理:其實就是將E盤下的文件數據讀取後,輸出到到D盤的指定文件中

步驟:

1.在D盤創建一個文件,用於存儲E盤的文件數據
2.定義輸入輸出流和E盤指定文件關聯
3.通過不斷的讀寫完成數據存儲
4.關閉流資源

第一種方式

示例代碼:

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CopyDemo {

    public static void main(String[] args) {
        FileWriter fw=null;
        FileReader fr=null;
        try {
            //實例化輸入輸出流
            fr=new FileReader("E:\\demo.txt");
            fw=new FileWriter("D:\\demo.txt");

            //將讀取到的數據寫入目標文件
            int ch=0;
            while((ch=fr.read())!=-1){
                fw.write(ch);
                //刷新流緩存
                fw.flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        finally{
            //關閉資源
            if(fr!=null)
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            if(fw!=null)
                try {
                    //關閉流
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }

    }

}

說明:每次讀取一個字符就寫入一個字符,效率較低。因此不建議使用這種方式。但如果目標文件較小倒也無所謂。

第二種方式

本例用到的方法:

int read(char[] cbuf)
將字符讀入數組。如果已到達流的末尾,則返回 -1
abstract void write(char[] cbuf, int off, int len)
寫入字符數組的某一部分。

示例代碼:

public static void main(String[] args) {
        FileWriter fw=null;
        FileReader fr=null;
        try {
            //實例化輸入輸出流
            fr=new FileReader("E:\\demo.txt");
            fw=new FileWriter("D:\\demo.txt");

            //定義用於臨時存儲數據的字符數組
            char[] bufArr=new char[1024];

            //存儲讀取到的字節長度
            int len=0;
            while((len=fr.read(bufArr))!=-1){
                //將字符數組的有效範圍寫入目標文件
                fw.write(bufArr,0,len);
                //刷新流緩存
                fw.flush();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
        finally{
            //關閉資源
            if(fr!=null)
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            if(fw!=null)
                try {
                    //關閉流
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }

    }

說明:這種方式的效率較高,推薦使用


4.緩衝字符流

1.緩衝字符流概述

  • 緩衝區的出現提高了對數據的讀寫效率
  • 緩衝區要結合流纔可以使用
  • 緩衝流在流的基礎上對流的功能進行了增強。

對應類:

BufferedWriter
BufferedReader

使用的緩衝區的時候的需要注意以下兩點:

1.在使用緩衝區之前必須先有流對象,並將流對象作爲參數構造一個緩衝區對象。

2.使用緩衝區時,一定要使用flush方法。另外,關閉緩衝區實際上就是關閉緩衝區中的流對象,因此在使用close對緩衝區進行關閉後,可以不必對流對象再調用close方法進行關閉。

2.BufferedWriter

BufferedWriter用於將文本寫入字符輸出流,緩衝各個字符,從而提供單個字符、數組和字符串的高效寫入.可以指定緩衝區的大小,或者接受默認的大小。在大多數情況下,默認值就足夠大了。

構造方法:

BufferedWriter(Writer out)
創建一個使用默認大小輸出緩衝區的緩衝字符輸出流。
BufferedWriter(Writer out, int sz)
創建一個使用給定大小輸出緩衝區的新緩衝字符輸出流。

常用方法:

void close()
關閉此流,但要先刷新它。
void flush()
刷新該流的緩衝。
void newLine()
寫入一個行分隔符,即換行符。並且該操作是跨平臺的。
void write(char[] cbuf)
寫入字符數組。
void write(char[] cbuf, int off, int len)
寫入字符數組的某一部分。
void write(int c)
寫入單個字符。
void write(String str)
寫入字符串。
void write(String s, int off, int len)
寫入字符串的某一部分。

示例代碼:

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class BufWriterDemo {

    public static void main(String[] args) {
        BufferedWriter bw=null;
        try {
            //通過傳入FileWriter對象實例化BufferedWriter
            bw=new BufferedWriter(new FileWriter("G:\\buf_test.txt"));
            bw.write("test:這是通過緩衝流寫入的數據");
            //通過緩衝流的特有方法寫入換行符
            bw.newLine();
            bw.write("test:通過緩衝流的方法寫入了換行符");
            //刷新流的緩存
            bw.flush();

        } catch (IOException e) {
            e.printStackTrace();
        }
        finally{
            if(bw!=null)
                try {               
                    //關閉流
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

}

程序運行結果:

3.png

3.BufferedReader

BufferedReader用於從字符輸入流中讀取文本,緩衝各個字符,從而實現字符、數組和行的高效讀取。

構造方法:

BufferedReader(Reader in)
創建一個使用默認大小輸入緩衝區的緩衝字符輸入流。
BufferedReader(Reader in, int sz)
創建一個使用指定大小輸入緩衝區的緩衝字符輸入流。

常用方法:

void close()
關閉該流並釋放與之關聯的所有資源。
int read()
讀取單個字符。
int read(char[] cbuf, int off, int len)
將字符讀入數組的某一部分。
String readLine()
讀取一個文本行,包含該行內容的字符串,不包含任何行終止符。如果已到達流末尾,則返回 null。
long skip(long n)
跳過字符。
void reset()
將流重置到最新的標記。

示例代碼:

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class BufReaderDemo {

    public static void main(String[] args) {
        BufferedReader br=null;
        try {
            ////通過傳入FileReader對象實例化BufferedReader
            br=new BufferedReader(new FileReader("G:\\buf_test.txt"));
            String str=null;
            while((str=br.readLine())!=null){
                //注意要使用println輸出獲取到的數據
                System.out.println(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally{
            if(br!=null)
                try {
                    //關閉流
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }
}

程序運行結果:

test:這是通過緩衝流寫入的數據
test:通過緩衝流的方法寫入了換行符

說明:對於獲取的每行數據,需要通過println輸出,因爲readLine()返回的字符串中不包含換行符。

4.通過緩衝區複製文本文件

需求描述: 通過緩衝區將E盤指定文件複製到D盤

通過readLine()writeLine()方法高效率讀取和寫出

注意:輸出時使用newLine()方法寫出換行符,因爲readLine()返回的字符串中不包含換行符,只返回有效內容。

示例代碼:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class BufCopyDemo {

    public static void main(String[] args) {
        BufferedWriter bw=null;
        BufferedReader br=null;

        try {
            //實例化輸入輸出緩衝流
            bw=new BufferedWriter(new FileWriter("D:\\buf_demo.txt"));
            br=new BufferedReader(new FileReader("E:\\buf_demo.txt"));

            String str=null;
            while((str=br.readLine())!=null){
                bw.write(str);
                //輸出換行符
                bw.newLine();
                //刷新緩衝輸出流緩存
                bw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally{
            if(bw!=null)
                try {               
                    //關閉流
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            if(br!=null)
                try {
                    //關閉流
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }

    }
}

5.自定義一個簡單的緩衝區Reader類

示例代碼:

import java.io.FileReader;
import java.io.IOException;

public class MyReader {
    private FileReader fr;//字符流,用於數據的讀取

    //構造方法
    public MyReader(FileReader fr){
        this.fr=fr;
    }

    //讀取整行數據的自定義方法
    public String MyReadLine() throws IOException{
        //實例化一個StringBuilder對象用來臨時存儲數據
        StringBuilder strBlr=new StringBuilder();

        int ch=0;       
        while((ch=fr.read())!=-1){
            //讀取到換行符時執行的操作
            if(ch=='\r')
                continue;
            //讀取到換行符時執行的操作
            if(ch=='\n')
                return strBlr.toString();   
            else
                strBlr.append((char)ch);
        }       
        //當讀取的文件中僅有一行數據且沒有換行符時,返回讀取到的字符串
        if(strBlr.length()!=0)
            return strBlr.toString();       

        return null;
    }

    //關閉流的方法
    public void close() throws IOException{ 
        fr.close(); 
    }
}

注意:以上的自定義類僅實現了緩衝流的兩個功能,可以利用裝飾設計模式將代碼優化。令該類繼承Reader接口,並且通過子類實現父類中的抽象方法。優化後的代碼如下。

示例代碼:

import java.io.FileReader;
import java.io.IOException;

public class MyReader {
    private FileReader fr;//字符流,用於數據的讀取

    //構造方法
    public MyReader(FileReader fr){
        this.fr=fr;
    }

    //讀取整行數據的自定義方法
    public String MyReadLine() throws IOException{
        //實例化一個StringBuilder對象用來臨時存儲數據
        StringBuilder strBlr=new StringBuilder();

        int ch=0;       
        while((ch=fr.read())!=-1){
            //讀取到換行符時執行的操作
            if(ch=='\r')
                continue;
            //讀取到換行符時執行的操作
            if(ch=='\n')
                return strBlr.toString();   
            else
                strBlr.append((char)ch);
        }       
        //當讀取的文件中僅有一行數據且沒有換行符時,返回讀取到的字符串
        if(strBlr.length()!=0)
            return strBlr.toString();       

        return null;
    }

    //關閉流的方法
    public void close() throws IOException{ 
        fr.close(); 
    }
}

5.LineNumberReader

LineNumberReader是跟蹤行號的緩衝字符輸入流。

構造方法:

LineNumberReader(Reader in)
使用默認輸入緩衝區的大小創建新的行編號 reader。
LineNumberReader(Reader in, int sz)
創建新的行編號 reader,將字符讀入給定大小的緩衝區。

常用方法:

int getLineNumber()
獲得當前行號。
void setLineNumber(int lineNumber)
設置當前行號。
int read()
讀取單個字符。
int read(char[] cbuf, int off, int len)
將字符讀入數組中的某一部分。
String readLine()
讀取文本行。
long skip(long n)
跳過字符。
void reset()
將該流重新設置爲最新的標記。

說明:LineNumberReader是基於Reader類,通過裝飾設計模式的一個裝飾類

示例代碼:

    //部分代碼
    public static void main(String[] args) {        
        LineNumberReader lnr=null;
        try {
            //實例化LineNumberReader對象
            lnr=new LineNumberReader(new FileReader("G:\\line_test.txt"));

            String str=null;
            while((str=lnr.readLine())!=null){
                //獲取行號
                int num=lnr.getLineNumber();
                if(num==6)
                    lnr.setLineNumber(7);
                System.out.println(num+"  "+str);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally{
            if(lnr!=null)
                try {
                    lnr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

程序運行結果:

1 test:這是用於測試LineNumberReader的文件
2 LineNumberReader可以輸出數據的行號
3 就像你現在看到的這樣
4 麼麼噠
5 而且還可以設置行號
6 就像這樣
8 我其實是第七行,不是第八行


相關閱讀:

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