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
