1. 程式人生 > 其它 >JavaIO流(輸入輸出操作)

JavaIO流(輸入輸出操作)

Java中執行輸出和輸入操作,需要通過IO流。例如最常見的System.out.println()就是一個輸出流。IO流的類比較多,但核心體系就是由File、InputStream、OutputStream、Reader、Writer和Serializable(介面)組成的,後續會一一詳細說明。

I/O流基礎概念

按照流的方向分為輸入流(InputStream)與輸出流(OuputStream):

  • 輸入流:只能讀取資料,不能寫入資料。
  • 輸出流:只能寫入資料,不能讀取資料。

因為程式是執行在記憶體中,以記憶體角度來理解輸入輸出概念,如下:

可以看到輸入與輸出是一個相對概念,資料寫入檔案,對於程式來說是輸出流,對檔案來說是輸入流。但一般是以程式作為中心,所以從程式寫入資料到其他位置,則是輸出流,將資料讀入程式中則是輸入流。

簡單的說就是:讀取資料就是輸入流,寫入資料就是輸出流。

按照處理的資料單位分為位元組流和字元流

  • 位元組流:操作的資料單元是8位的位元組。InputStream、OutputStream作為抽象基類。
  • 字元流:操作的資料單元是字元。以Writer、Reader作為抽象基類。
  • 位元組流可以處理所有資料檔案,若處理的是純文字資料,建議使用字元流。

IO流中的三類資料來源

  • 基於磁碟檔案:FileInputStream、FileOutputSteam、FileReader、FileWriter
  • 基於記憶體:ByteArrayInputStreamByteArrayOutputStream(ps:位元組陣列都是在記憶體中產生)
  • 基於網路:SocketInputStream、SocketOutputStream(ps:網路通訊時傳輸資料)

根據流的作用可分為節點流和處理流

節點流:程式直接與資料來源連線,和實際的輸入/輸出節點連線;處理流:對節點流進行包裝,擴充套件原來的功能,由處理流執行IO操作。

處理流的作用和分類:

處理流可以隱藏底層裝置上節點流的差異,無需關心資料來源的來源,程式只需要通過處理流執行IO操作。處理流以節點流作為構造引數。通常情況下,推薦使用處理流來完成IO操作。

緩衝流:提供一個緩衝區,能夠提高輸入/輸出的執行效率,減少同節點的頻繁操作。例如:BufferedInputStream/BufferedOutputStream、BufferedReader/BufferWriter

轉換流:將位元組流轉成字元流。位元組流使用範圍廣,但字元流更方便。例如一個位元組流的資料來源是純文字,轉成字元流來處理會更好。InputStreamReader/OutputStreamWriter

列印輸出流:列印輸出指定內容,根據構造引數中的節點流來決定輸出到何處。

PrintStream :列印輸出位元組資料。
PrintWriter : 列印輸出文字資料。 

附圖:JavaIO體系的全體類

介紹完基礎概念後,使用IO流來完成一些簡單功能:

(一)使用位元組流讀取本地檔案

//File物件定位資料來源
public static void getContent(File file) throws IOException { 
//建立檔案緩衝輸入流
file BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); 
byte[] buf = new byte[1024];//建立位元組陣列,儲存臨時讀取的資料 
int len = 0;//記錄資料讀取的長度
 //迴圈讀取資料
 while((len = bis.read(buf)) != -1) { //長度為-1則讀取完畢 
 System.out.println(new String(buf,0,len));    
}
 bis.close(); //關閉流
}

【技巧】如果資料來源是純文字資料,使用字元流效率更高。

(二)使用字元處理流讀取本地檔案內容

    public static void getContent(String path) throws IOException {
        File f = new File(path); 
        if (f.exists()) { // 判斷檔案或目錄是否存在
            if (f.isFile()) { 
                BufferedReader br = new BufferedReader(new FileReader(path));//該緩衝流有一個readLine()獨有方法
                String s = null;
                while ((s = br.readLine()) != null) {//readLine()每次讀取一行
                    System.out.println(s);
                }
            }
        }
    }

該方法比上一個增加了檔案判斷,提高了程式的健壯性。使用了BufferedReader處理流來處理純文字資料,比位元組流更加簡潔方便。

(三)使用字元流寫入資料到指定檔案:

public static void main(String[] args) throws IOException {
        //以標準輸入作為掃描來源
        Scanner sc = new Scanner(System.in);
        File f = new File("D:\\reviewIO\\WRITERTest.txt");
        BufferedWriter bw = new BufferedWriter(new FileWriter(f));
        if(!f.exists()) {
            f.createNewFile();
        }
        while(true) {
            String s = sc.nextLine();
            bw.write(s);
            bw.flush();
            if(s.equals("結束") || s.equals("")) {
                System.out.println("寫入資料結束!");
                return;
            }
        }
    }

(四)使用轉換流(InputStreamReader/OutputStreamWriter),對寫入資料進行改進:

public static void testConvert(File f) throws IOException {
        if(!f.exists()) {
            f.createNewFile();
        }
        //以System.in作為讀取的資料來源,即從鍵盤讀取
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new FileWriter(f,true)); //允許新增內容,不會清除原有資料來源
        String s = null;
        while(!(s = br.readLine()).equals("")) {
            bw.write(s);
            bw.newLine();//空一行
        }
        bw.flush();        
        bw.close();
        br.close();
    } 

因為System.in是一個InputStream物件,緩衝字元流無法直接使用,需要通過轉換流將位元組流轉成字元流。然後使用字元輸入處理流的readLine()每次讀取一行,使用newLine()完成換行。

注意點:通常使用IO流寫入檔案時,寫入的資料總會覆蓋原來的資料,這是因為檔案輸出流預設不允許追加內容,所以需要為FileOuputStream、FileWriter的構造引數boolean append 傳入true。

(五)使用位元組流完成檔案複製
//位元組流實現檔案拷貝
    public static String copyFile(String src, String dest) throws IOException, ClassNotFoundException {
        File srcFile = new File(src);//原始檔資料來源
        File desFile = new File(dest);//寫入到目標資料來源
        //資料來源不存在
        if(!srcFile.exists() || !desFile.exists()) {
            throw new ClassNotFoundException("原始檔或者拷貝目標檔案地址不存在!");
        }
        //非檔案型別
        if(!srcFile.isFile() || !desFile.isFile()) {
            return "原始檔或者目標檔案不是檔案型別!";
        }
        InputStream is = null;
        OutputStream os = null;
        byte[] buf = new byte[1024];//快取區
        int len = 0;//讀取長度
        try {
            is = new BufferedInputStream(new FileInputStream(srcFile));//讀取資料來源
            os = new BufferedOutputStream(new FileOutputStream(desFile));//寫入到資料來源            
            while((len = is.read(buf)) != -1) { //讀取長度不為-1,繼續讀取
                os.write(buf); //讀取內容之後馬上寫入目標資料來源
            }
            os.flush();//輸出
            return "檔案拷貝成功!檢視拷貝檔案路徑:" + desFile.getPath();                        
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(is != null)
                is.close();
            if(os != null)
                os.close();
        }
        return "檔案拷貝失敗";
    }

(六)使用列印流來完成寫入資料操作:

        //輸出內容的檔案資料來源
        File f = new File("D:\\reviewIO\\PW.java");
        PrintWriter pw = new PrintWriter(f);
        //把指定內容列印至資料來源中
        pw.println("AAAAAAAAA");
        pw.println("BBBBBBBBB");
        pw.println("CCCCCCCCC");
        pw.flush();
        System.out.println("使用PrintWriter寫入資料完成");
        System.out.println("==========讀取寫入的資料==========");
        BufferedReader br = new BufferedReader(new FileReader(f));
        String s = null;
        StringBuilder sb = new StringBuilder();//一個可變字串
        while((s = br.readLine()) != null) {
            sb.append(s); //把讀取的字串組合起來
        }
        System.out.println(sb);
        br.close();
        pw.close();

一般情況下,若是輸出文字資料,建議使用列印流。PrintWriter還可以指定輸出文字使用何種字符集、在構造引數中指定是否自動重新整理。如果不想覆蓋原來的資料,使用該類的append()方法,就會在檔案尾部新增內容。

(七)使用列印流來完成文字拷貝:

    // 使用列印流PrintStream來完成檔案拷貝
    public static void copyFile(File src, File dest) throws Exception {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));
        PrintStream ps = new PrintStream(bos, true);
        byte[] buf = new byte[1024];
        int len = 0;
        //迴圈讀取資料,然後寫入到目標檔案
        while ((len = bis.read(buf)) != -1) {
            ps.write(buf);
        }
        ps.close();
        bos.close();
    }

列印流實現檔案拷貝操作和位元組流差不多,除了用到列印流建構函式的不自動重新整理。列印流還有一個好處就是無需檢查異常。

轉自:https://www.cnblogs.com/fwnboke/p/8529492.html