本文內容:IO流操做文件的細節分析;分析各類操做文件的方式。java
從一個示例開始分析,如何操做文件:數組
/** * 向一個文件中寫入數據 * @throws IOException */ private static void writeFile() throws IOException { File file = new File("D://tmp/a.txt"); try (OutputStream outputStream = new FileOutputStream(file, false)) { outputStream.write("ABC".getBytes(StandardCharsets.UTF_8)); } } /** * 從一個文件中讀取數據 * @throws IOException */ private static void readFile() throws IOException { File file = new File("D://tmp/a.txt"); try (FileInputStream inputStream = new FileInputStream(file)) { StringBuilder stringBuffer = new StringBuilder(); int count; while ((count = inputStream.read()) != -1) { stringBuffer.append((char) count); } System.out.println(stringBuffer.toString()); } }
向一個文件寫入數據時,首先新建一個文件,而後定義一個輸出流,爲何數輸出流?Java裏的API都是面向JVM的,輸入和輸出也是面向jvm的,輸入就是向jvm中輸入,輸出就是從jvm中輸出;
outputStream.write() 方法將一個字節數組寫入到a.txt中,執行完該方法後文件中就有內容了。程序是如何寫進去的?app
/** * Opens a file, with the specified name, for overwriting or appending. * @param name name of file to be opened * @param append whether the file is to be opened in append mode */ private native void open0(String name, boolean append) throws FileNotFoundException; /** * Writes the specified byte to this file output stream. * * @param b the byte to be written. * @param append {@code true} if the write operation first * advances the position to the end of file */ private native void write(int b, boolean append) throws IOException; /** * Writes a sub array as a sequence of bytes. * @param b the data to be written * @param off the start offset in the data * @param len the number of bytes that are written * @param append {@code true} to first advance the position to the * end of file * @exception IOException If an I/O error has occurred. */ private native void writeBytes(byte b[], int off, int len, boolean append) throws IOException; private native void close0() throws IOException;
從源碼上看,文件寫入須要三個步驟:一、打開一個文件,也就是獲取文件的句柄;二、向文件中輸出字節流;三、關閉輸出流,釋放資源;
open0(String name, boolean append) 這個本地方法就是打開文件的方法,調用了操做系統的API。
write(int b, boolean append)這個本地方法是最終的寫入的方法,參數有兩個:b[],append; b表明的是一個字節數組,append表明是否添加,false則會覆蓋初始文件。jvm
與寫入文件相似,文件讀取一樣也是相對與jvm,文件讀取是向jvm中輸入,因此是InputStream;ui
private native int read0() throws IOException; /** * Reads a subarray as a sequence of bytes. * @param b the data to be written * @param off the start offset in the data * @param len the number of bytes that are written * @exception IOException If an I/O error has occurred. */ private native int readBytes(byte b[], int off, int len) throws IOException;
讀取文件也是兩種方法,一個字節一個字節地讀,也能安裝字節數組去讀。步驟與文件寫入相似:一、打開一個文件,也就是獲取文件的句柄;二、從文件中讀取字節流;三、關閉輸入流,釋放資源;this
按照字節寫入,是直接調用native方法獲取文件句柄操做文件,而且是直接寫入字節數組,可是若是是寫入中文,就不同了,UTF-8編碼中漢字是3個字節,英文字母是1個字節;固然不一樣的編碼方式還不同。這樣寫入不會有問題,可是讀取的時候,問題就來了,漢字是3個字節,無論是按照字節數組仍是字節去讀,均可能只讀取了一個漢字的一半,這樣讀取出來的字節數組轉成可視化的內容就會出現亂碼。編碼
若是是從一個輸入流到另一個輸出流,好比文件導出,就可使用輸入流讀取的字節數組直接放到輸出流中去。注意:讀取到文件最後會返回-1,能夠以此爲分界點。操作系統
private static void writeFile0() throws IOException { File fileA = new File("D://tmp/a.txt"); File fileB = new File("D://tmp/b.txt"); try (FileInputStream inputStream = new FileInputStream(fileA); OutputStream outputStream = new FileOutputStream(fileB)) { int count; byte[] bytes = new byte[64]; while ((count = inputStream.read(bytes)) != -1) { outputStream.write(bytes, 0, count); } } }
在Java API中提供了另一種方式操做文件,那就是按照字符讀取,也能按行讀取。這種讀取方式在文件和jvm中間加了一層緩衝區。code
private static void readFile1() throws IOException { File file = new File("D://tmp/a.txt"); try (FileReader fileReader = new FileReader(file)) { BufferedReader bufferedReader = new BufferedReader(fileReader); int count; StringBuilder stringBuilder = new StringBuilder(); while ((count = bufferedReader.read()) != -1) { // 還能夠按行讀取bufferedReader.readLine(); stringBuilder.append((char) count); } System.out.println(stringBuilder.toString()); } }