Java中的IO操作涉及到的概念及相關類很多,很容易弄混,今天特來整理總結一下,並附上一份完整的文件操作的代碼。
概念解析
讀和寫
流就是管道,向管道里面寫數據用輸出流:write
從管道里面讀數據,用輸入流:read
流的分類
流,按照不同的維度有不同的分類,按照從流中每次讀取數據單位不同,划分為字節流字符流。
按照流-對應操作的角色的不同(是IO設備,還是其他流)是否能夠直接向特定的IO設備如文件、網絡、磁盤等,能夠直接與這些進行讀寫的稱之為節點流,
對節點流進一步封裝的流,通過封裝后的流來實現讀寫功能,處理流也被稱為高級流。
注意事項
1、流和數組不一樣,不能通過索引讀寫數據。在流中,你也不能像數組那樣前后移動讀取數據,除非使用RandomAccessFile 處理文件。
2、流僅僅只是一個連續的數據流。流中的數據只能夠順序訪問。當達到流末尾時,返回-1。
3、InputStream的read()方法返回一個字節大小,返回值的范圍在0到255之間。
4、Reader的read()方法返回一個字符,會根據文本的編碼,一次讀取一個或者多個字節,返回值的范圍在0到65535之間。
5、read(byte[])會嘗試讀取與給定字節數組容量一樣大的字節數,返回值int表示已經讀取過的字節數。如果InputStream內可讀的數據不足以填滿字節數組,那么數組剩余的部分將包含本次讀取之前的數據。記得檢查有多少數據實際被寫入到了字節數組中。
6、read(byte, int offset, int length)同樣將數據讀取到字節數組中,不同的是,該方法從數組的offset位置開始,並且最多將length個字節寫入到數組中。同樣地,read(byte, int offset, int length)方法返回一個int變量,表示有多少字節已經被寫入到字節數組中,所以請記得在讀取數據前檢查上一次調用read(byte, int offset, int length)的返回值。
示例代碼
package com.lingyejun.io; import java.io.*; /** * Created by Lingye on 2018/9/27 20:15 */ public class FileStreamOperate { public static final String READ_UTF8_FILE_PATH = "D:\\input-utf8.txt"; public static final String READ_UNICODE_FILE_PATH = "D:\\input-unicode.txt"; public static final String WRITE_BYTES_FILE_PATH = "D:\\output-bytes.txt"; public static final String WRITE_CHARS_FILE_PATH = "D:\\output-char.txt"; /** * 按照字節流的方式讀取文件內容 * * Step 1.根據文件路徑,構建文件對象 * Step 2.創建輸入流用來讀取文件內容 * Step 3.創建字節數組來存放讀取內容 * Step 4.關閉讀取文件的輸入流 * * @return */ public void readFileByFileInputStream() { System.out.println("=== readFileByFileInputStream Start ==="); // 構建文件對象 File inputFile = new File(READ_UTF8_FILE_PATH); // 初始化輸入流 InputStream inputStream = null; try { // 創建字節輸入流 inputStream = new FileInputStream(inputFile); // 讀取到1KB字節數組中 byte[] buffer = new byte[100]; // 每次讀取的字節數 int readLength; // 讀取數據並放到buffer數組中 while ((readLength = inputStream.read(buffer)) != -1) { // UTF-8為變長編碼,一個漢字占3個字節 System.out.println("本次讀取" + readLength + "個字節數據內容為:" + new String(buffer)); } } catch (FileNotFoundException e) { // 文件未找到時異常處理 e.printStackTrace(); } catch (IOException e) { // 讀取過程中,刪除文件會出此異常 e.printStackTrace(); } finally { if (inputStream != null) { try { // 關閉流過程,也有可能出現異常 inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("=== readFileByFileInputStream End ==="); } /** * 按照字符流的方式讀取文件內容 * * Step 1.根據文件路徑,構建文件對象 * Step 2.創建字符輸入流讀取文件內容 * Step 3.創建字符數組來存放讀取內容 * Step 4.關閉讀取文件的字符輸入流 * * @return */ public void readFileByFileReader(){ System.out.println("=== readFileByFileReader Start ==="); // 根據路徑拿到文件對象 File file = new File(READ_UTF8_FILE_PATH); // 初始化字符輸入流 Reader fileReader = null; // 初始化存放讀取內容的字符數組 char[] charArray = new char[100]; // 初始化一個字符 char once; try { fileReader = new FileReader(file); // 一次讀取一個數組長度的字符串 fileReader.read(charArray); System.out.println(charArray); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (fileReader != null) { try { // 關閉流過程,也有可能出現異常 fileReader.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("=== readFileByFileReader End ==="); } /** * 通過字節流的方式寫入信息到文件 * * Step 1.根據文件路徑,構建文件對象 * Step 2.創建字節輸出流寫出信息到文件 * Step 3.構造待寫出的內容,並轉為字節數組 * Step 4.關閉讀取文件的字符輸出流 */ public void writeFileByFileOutputStream() { System.out.println("=== writeFileByFileOutputStream Start ==="); // 創建寫出文件 File file = new File(WRITE_BYTES_FILE_PATH); // 初始化字節輸出流 OutputStream outputStream = null; // 寫出內容 String outInfo = "寫出測試"; // 轉成字節數組 byte[] byteArray = outInfo.getBytes(); try { // 創建輸出字節流 outputStream = new FileOutputStream(file); outputStream.write(byteArray); System.out.println("按照字節流成功寫出內容:"+outInfo); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (outputStream != null) { try { // 關閉寫出流時,注意抓異常 outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("=== writeFileByFileOutputStream End ==="); } /** * 通過字符流的方式寫入信息到文件 * * Step 1.根據文件路徑,構建文件對象 * Step 2.創建字符輸出流寫出信息到文件 * Step 3.構造待寫出的內容,並轉為字符數組 * Step 4.關閉讀取文件的字符輸出流 */ public void writeFileByFileWriter(){ System.out.println("=== writeFileByFileWriter Start ==="); // 創建寫出文件 File file = new File(WRITE_CHARS_FILE_PATH); // 初始化字符輸出流 Writer fileWriter = null; String strInfo = "字符寫出數據"; try { // 創建輸出字符流 fileWriter = new FileWriter(file); // 寫出內容 fileWriter.write(strInfo); System.out.println("按照字符流成功寫出內容:"+strInfo); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if(fileWriter != null){ try { // 關閉寫出流時,注意抓異常 fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } System.out.println("=== writeFileByFileWriter End ==="); } /** * 任意讀取文件內容 */ public void randomAccessFile(){ System.out.println("=== randomAccessFile Start ==="); File file = new File(READ_UTF8_FILE_PATH); try { RandomAccessFile randomAccessFile = new RandomAccessFile(file,"r"); // 獲取文件當前的指針位置 System.out.println("file now pointer is "+randomAccessFile.getFilePointer()); // 將文件指針設置到指定位置(絕對位置) randomAccessFile.seek(3); // 相對位置,相對於當前的位置, randomAccessFile.skipBytes(3); System.out.println("file now pointer is "+randomAccessFile.getFilePointer()); // 字節數組 byte[] buffer = new byte[17]; // off是指的寫到buffer的數組的起始位置 randomAccessFile.read(buffer,0,buffer.length); System.out.println(new String(buffer)); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } System.out.println("=== randomAccessFile End ==="); } /** * 通過字節流的方式將文件內容拷貝到另一個文件中 * * Step 1.根據文件路徑,構建源文件對象 * Step 2.根據文件路徑,構造目的文件對象 * Step 3.創建字節輸入流從源文件中讀取信息 * Step 4.將讀入到內存的信息再寫出到目的文件中 * Step 5.拷貝完成后關閉輸入輸出流 */ public void copyFile() { System.out.println("=== copyFile Start ==="); // 輸入文件對象 File inFile = new File(READ_UTF8_FILE_PATH); // 輸出文件對象 File outFile = new File(WRITE_BYTES_FILE_PATH); // 初始化輸入流 InputStream inputStream = null; // 初始化輸出流 OutputStream outputStream = null; try { // 將輸入流懟到輸入文件,使程序內存與磁盤建立聯系 inputStream = new FileInputStream(inFile); // 將輸出流懟到輸出文件,使程序內存與磁盤建立聯系 outputStream = new FileOutputStream(outFile); while (true) { // 讀取信息到內存 int temp = inputStream.read(); // 拷貝完成 if (temp == -1) { break; } // 將內容拷貝到輸出文件中 outputStream.write(temp); } System.out.println("拷貝文件成功完成"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (inputStream != null) { try { // 關閉輸入流異常后,也要保證輸出流關閉 inputStream.close(); } catch (IOException e) { e.printStackTrace(); } finally { if (outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } } System.out.println("=== copyFile End ==="); } public static void main(String[] args) { FileStreamOperate fileStreamOperate = new FileStreamOperate(); // 按照字節流讀取的話,一個漢字兩個字節 fileStreamOperate.readFileByFileInputStream(); // 按照字符讀取,java Unicode編碼一個字符(不論字母還是漢字)兩個字節 fileStreamOperate.readFileByFileReader(); // 采用任意讀取的方式讀取文件信息 fileStreamOperate.randomAccessFile(); // 按照字節流的方式寫信息到文件 fileStreamOperate.writeFileByFileOutputStream(); // 按照字符流的方式寫信息到文件 fileStreamOperate.writeFileByFileWriter(); // 拷貝文件 fileStreamOperate.copyFile(); } }
輸入輸出文件和執行結果
文件展示
執行結果
小結
通過本篇,我們學會了文件IO的一些常規操作方式,但是每次讀取完文件后都需要在最后進行關閉流資源的close操作,這個操作不可省略,但是每次都寫又顯得十分啰嗦,有沒有更加簡便的方式呢,請看我的下一篇文章《try with resources簡潔的異常捕獲機制》。
參考文章:https://blog.csdn.net/yhl_jxy/article/details/79272792