Java IO流操作

smartsean發表於2018-01-24

首先我們得知道:我們儲存在硬碟上的檔案是byte byte byte...儲存的,是資料的集合。

java 中的 io 流主要分為:

  • 位元組流
  • 字元流

一、位元組流

位元組流又分為

  • 輸入流 InputStream
  • 輸出流 OutputStream

輸入流負責從 源(可以是檔案) 讀取資料到 Java程式 中。

輸出流負責把 Java程式 中資料寫入到 源(可以是檔案) 中。

1.1 輸入流 InputStream

InputStream 是一個抽象類,抽象了應用程式讀取資料的方式。

輸入流基本方法:

  • int b = in.read();讀取一個位元組無符號填充到int低八位.-1是 EOF
  • in.read(byte[] buf)
  • in.read(byte[] buf,int start,int size)

常用的實現類有以下三個

1.1.1 FileInputStream

FileInputStream 用於從檔案讀取資料,可以通過 new 關鍵字構造,比如:

File file = new File(檔案路徑);
FileInputStream inputStream = new FileInputStream(file);
複製程式碼

也可以:

FileInputStream inputStream = new FileInputStream(檔案路徑);
複製程式碼

使用示例: 本示例從專案根目錄的 test.txt 讀取內容並列印,

public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
        FileInputStream inputStream = new FileInputStream("test.txt");
        int len;
        byte[] bytes = new byte[8 * 1024];
        while ((len=inputStream.read(bytes,0,bytes.length))!=-1){
            String s = new String(bytes,0,len,"utf-8");
            System.out.println(s);
        }
        inputStream.close();
    }
}
複製程式碼

1.1.1 DataInputStream

DataInputStream 對"流"功能的擴充套件,可以更加方便的讀取 int, long,字元等型別資料。

使用 Demo 如下:

public class DataInputStreamDemo {

    public static void main(String[] args) throws IOException {
        FileInputStream inputStream = new FileInputStream("test.txt");
        DataInputStream dataInputStream = new DataInputStream(inputStream);

        int len;
        byte[] buffer = new byte[8 * 1024];
        while ((len = dataInputStream.read(buffer, 0, buffer.length)) != -1) {
            System.out.println(new String(buffer,0,len));
        }
        inputStream.close();
        dataInputStream.close();
    }
}
複製程式碼

1.1.3 BufferedInputStream

BufferedInputStream 為 IO 提供了帶緩衝區的操作,一般開啟檔案進行寫入 或讀取操作時,都會加上緩衝,這種流模式提高了IO的效能。

使用如下:

public class BufferedInputStreamDemo {
    public static void main(String[] args) throws IOException {
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("test.txt"));
        int len;
        byte[] buffer = new byte[8*1024];
        while ((len=bufferedInputStream.read(buffer,0,buffer.length))!=-1){
            System.out.println(new String(buffer,0,len));
        }
        bufferedInputStream.close();
    }
}
複製程式碼

1.2 輸出流 OutputStream

OutputStream 也是一個抽象類,抽象了應用程式寫資料的方式。

輸出流基本方法:

  • out.write(int b) 寫出一個byte到流,b的低8位
  • out.write(byte[] buf)將buf位元組陣列都寫入到流
  • out.write(byte[] buf,int start,int size)

同樣,常用的輸出流實現類有下面三個:

1.2.1 FileOutputStream

FileOutputStream 用於寫入資料到檔案中,可以通過 new 關鍵字很輕鬆的建立

File file = new File(檔案路徑);
FileOutputStream outputStream = new FileOutputStream(file);
複製程式碼

或者

FileOutputStream outputStream = new FileOutputStream(檔案路徑);
複製程式碼

使用示例: 本示例是寫文字到專案根目錄的檔案 test.txt。

public class FileOutputStreamDemo {
    public static void main(String[] args) throws IOException {
        FileOutputStream outputStream = new FileOutputStream("test.txt");
        String  s = "測試FileOutputStreamDemo";
        outputStream.write(s.getBytes("utf-8"));
        outputStream.close();
    }
}
複製程式碼

1.2.2 DataOutputStream

DataOutputStream 對"流"功能的擴充套件,可以更加方便的寫入 int, long,字元等型別資料。

使用 demo 如下:

public class DataOutputStreamDemo {
    public static void main(String[] args) throws IOException {

        FileOutputStream outputStream = new FileOutputStream("test.txt");
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
        dataOutputStream.writeInt(123321);
        dataOutputStream.writeBoolean(true);
        dataOutputStream.writeUTF("DataOutputStreamDemo測試");
        outputStream.close();
        dataOutputStream.close();
    }
}
複製程式碼

如果按照上面執行是會發現,只能在 test.txt 檔案中看到

DataOutputStreamDemo測試
複製程式碼

前面寫入的 123321 和 true 都是亂碼,比如我這邊看到的是

亂碼

那是因為 DataOutputStream 是一種格式化的資料輸出方式,而並非都是字元流,如果寫到檔案中他的資料格式就和在記憶體中一樣,這樣他讀出來是會很方便,我們開啟檔案看到的內容是字元編碼的,int、boolean 不是字元編碼的,所以會亂碼。

然後使用 dataOutputStream.writeUTF 可以正確顯示 是因為 UTF-8的字元編碼是對的。

注意: 用 DataOutputStream 輸出的資料並不是為了用記事本開啟看的而是為了儲存資料的 一般來儲存為.dat檔案區別開文字本件。

1.2.3 BufferedOutputStream

BufferedOutputStream 為 IO 寫操作提供了帶緩衝區的操作,提高了IO的效能。

使用如下:

public class BufferedOutputStreamDemo {
    public static void main(String[] args) throws IOException {

        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("test.txt"));
        bufferedOutputStream.write("BufferedOutputStreamDemo測試".getBytes("utf-8"));
        bufferedOutputStream.close();
    }
複製程式碼

位元組輸入流先寫這麼多,還有很多 InputStream 和 OutputStream 的實現類,有興趣的可以去看下。

接下來我們通過一個例項,看看使用哪種方式操作更快。

public class IoUtil {

    /**
     * 檔案拷貝,單位元組、不帶緩衝進行檔案拷貝
     *
     * @param srcFile
     * @param destFile
     * @throws IOException
     */
    public static void copyFileByByte(File srcFile, File destFile) throws IOException {
        if (!srcFile.exists()) {
            throw new IllegalArgumentException("srcFile is not exists!");
        }
        if (!srcFile.isFile()) {
            throw new IllegalArgumentException("srcFile is not files");
        }
        FileInputStream in = new FileInputStream(srcFile);
        FileOutputStream out = new FileOutputStream(destFile);
        int len;
        while ((len = in.read()) != -1) {
            out.write(len);
            out.flush();
        }
        in.close();
        out.close();
    }

    /**
     * 檔案拷貝,位元組批量讀取
     *
     * @param srcFile
     * @param destFile
     * @throws IOException
     */
    public static void copyFile(File srcFile, File destFile) throws IOException {
        if (!srcFile.exists()) {
            throw new IllegalArgumentException("srcFile is not exists!");
        }
        if (!srcFile.isFile()) {
            throw new IllegalArgumentException("srcFile is not files");
        }
        FileInputStream in = new FileInputStream(srcFile);
        FileOutputStream out = new FileOutputStream(destFile);
        byte[] buf = new byte[8 * 1024];
        int len;
        while ((len = in.read(buf, 0, buf.length)) != -1) {
            out.write(buf, 0, len);
            out.flush();//最好加上
        }
        in.close();
        out.close();

    }

    /**
     * 檔案拷貝,使用帶緩衝的位元組流進行
     *
     * @param srcFile
     * @param destFile
     */
    public static void copyFileByBuffer(File srcFile, File destFile) throws IOException {
        if (!srcFile.exists()) {
            throw new IllegalArgumentException("srcFile is not exists!");
        }
        if (!destFile.isFile()) {
            throw new IllegalArgumentException("srcFile is not files");
        }
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(destFile));
        int len;
        while ((len = bufferedInputStream.read()) != -1) {
            bufferedOutputStream.write(len);
            bufferedOutputStream.flush();
        }
        bufferedOutputStream.close();
        bufferedOutputStream.close();
    }
}
複製程式碼

接下來我們分別使用三種方式拷貝一份檔案,看看用時分別多少

測試程式碼:

public class Main {

    public static void main(String[] args) throws IOException {
        long start1 = System.currentTimeMillis();
        IoUtil.copyFileByByte(new File("alfred.dmg"), new File("alfred1.dmg"));
        long end1 = System.currentTimeMillis();
        System.out.println("使用單位元組、不帶緩衝拷貝檔案用時:" + (end1 - start1));

        long start2 = System.currentTimeMillis();
        IoUtil.copyFile(new File("alfred.dmg"), new File("alfred2.dmg"));
        long end2 = System.currentTimeMillis();
        System.out.println("使用位元組批量讀取拷貝檔案用時:" + (end2 - start2));
        
        long start3 = System.currentTimeMillis();
        IoUtil.copyFileByBuffer(new File("alfred.dmg"), new File("alfred3.dmg"));
        long end3 = System.currentTimeMillis();
        System.out.println("使用使用帶緩衝的位元組流拷貝檔案用時:" + (end3 - start3));
    }
}
複製程式碼

執行看看:

使用單位元組、不帶緩衝拷貝檔案用時:28138
使用位元組批量讀取拷貝檔案用時:8
使用使用帶緩衝的位元組流拷貝檔案用時:22406
複製程式碼

是不是很恐怖,使用位元組批量讀取的速度快的不可思議,所以拷貝檔案時使用哪種方式,心裡應該有數了。

總結下: 可以看到,位元組流的實現類一般都是成對出現的,上面講了三對:

  • FileInputStream 和 FileOutputStream
  • DataInputStream 和 DataOutputStream
  • BufferedInputStream 和 BufferedOutputStream

二、字元流

字元流又分為

輸入流 Writer 輸出流 Reader

輸入流負責從 源(可以是檔案) 讀取資料到 Java程式 中。

輸出流負責把 Java程式 中資料寫入到 源(可以是檔案) 中。

位元組流和字元流很類似,Writer 和 Reader的實現類一般也是成對出現的。

2.1 OutputStreamWriter 和 InputStreamReader

使用如下:

public class IsrAndOswDemo {
    public static void main(String[] args) throws IOException {

        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("test1.txt"));
        outputStreamWriter.write("IsrAndOswDemo測試");
        outputStreamWriter.close();

        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("test1.txt"));
        while (inputStreamReader.ready()) {
            System.out.print((char) inputStreamReader.read());
        }
        int len;
        char[] bytes = new char[8 * 1024];
        //批量讀取,放入bytes這個字元陣列,從第0個位置開始放置,最多放bytes.length個
        //返回的是讀到的字元的個數
        while ((len = inputStreamReader.read(bytes, 0, bytes.length)) != -1) {
            String s = new String(bytes, 0, len);
            System.out.println(s);
        }
        inputStreamReader.close();
    }
}
複製程式碼

輸出:

IsrAndOswDemo測試
複製程式碼

2.2 BufferedReader 、BufferedWriter、PrintWriter

下面的例子是通過 BufferedReader 讀取 test1.txt 中的內容,然後分別寫入到 test4.txt 和 test5.txt 中。

public class BrAndBwAndPwDemo {
    public static void main(String[] args) throws IOException {
        
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("test1.txt")));

        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("test4.txt")));

        PrintWriter printWriter = new PrintWriter("test5.txt");

        String line;
        //一次讀一行,不識別換行
        while ((line=bufferedReader.readLine())!=null){
            System.out.println(line);
            // 這裡是BufferedWriter的寫操作
            bufferedWriter.write(line);
            bufferedWriter.flush();
            bufferedWriter.newLine();//單獨寫出換行操作

            // 這裡是PrintWriter的寫操作
            printWriter.print(line);//不換行
            printWriter.println(line);//換行
        }
        bufferedReader.close();
        bufferedWriter.close();
        printWriter.close();
    }
}

複製程式碼

你可以通過以下方式關注我:

  1. CSDN
  2. 掘金
  3. 個人部落格
  4. Github