如果要進行文件內容的操作,必須依靠數據流完成,而數據流分為兩種:
字節流:InpuStream(字節輸入流)、OutputStream(字節輸出流)
字符流:Reader(字符輸入流)、Writer(字符輸出流)
字符比字節處理的多,但使用哪個,基本流程都一樣
范例:
創建File類對象,主要是指明要操作的文件路徑
通過字節流或字符流的子類為父類實例化
進行文件的讀寫操作
關閉數據流(close())
字節輸出流:OutputStream
字節輸出流主要以byte數據為主
輸出單個字節:public abstract void write(int b) throws IOException
輸出全部字節數組:public void write(byte[] b) throws IOException
輸出部分字節數組:public void write(byte[] b, int off, int len) throws IOException //重點
OutputStream是抽象類,文件的輸出操作需要子類FileOutputStream,此類有兩個常用構造
構造方法:public FileOutputStream(File file) throws FileNotFoundException 新內容覆蓋文件
構造方法:public FileOutputStream(File file, boolean append) throws FileNotFoundException 追加文件內容
范例:實現文件的輸出(往文件里寫內容)
import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class Hello{ public static void main(String[] args) throws Exception { //第一步:定義要輸出文件的File類對象 File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); //輸出信息的時候文件可以不存在,目錄必須存在 if(!file.getParentFile().exists()) { //父路徑不存在 file.getParentFile().mkdirs(); //創建父路徑 } //第二部:利用OutputStream的子類為父類實例化 OutputStream output = new FileOutputStream(file); //第三步:輸出文字信息 String msg = "******"; //字符串 //為了方便輸出,需要將字符串變為字節數組 byte data[] = msg.getBytes(); // 變為了字節數組 output.write(data); //輸出數據 , 創建了test.TXT,並寫入了*****數據 output.close(); } }
實現只輸出部分內容:
import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class Hello{ public static void main(String[] args) throws Exception { //第一步:定義要輸出文件的File類對象 File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); //輸出信息的時候文件可以不存在,目錄必須存在 if(!file.getParentFile().exists()) { //父路徑不存在 file.getParentFile().mkdirs(); //創建父路徑 } //第二部:利用OutputStream的子類為父類實例化 OutputStream output = new FileOutputStream(file); //第三步:輸出文字信息 String msg = "adfasdfadsfasdfasdfsadfasdf"; //字符串 //為了方便輸出,需要將字符串變為字節數組 byte data[] = msg.getBytes(); // 變為了字節數組 output.write(data, 0, 10); //輸出數據 , 從0開始輸出10個 output.close(); }
使用循環的方式單個字節的輸出
import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class Hello{ public static void main(String[] args) throws Exception { //第一步:定義要輸出文件的File類對象 File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); //輸出信息的時候文件可以不存在,目錄必須存在 if(!file.getParentFile().exists()) { //父路徑不存在 file.getParentFile().mkdirs(); //創建父路徑 } //第二部:利用OutputStream的子類為父類實例化 OutputStream output = new FileOutputStream(file); //第三步:輸出文字信息 String msg = "adfasdfadsfasdfasdfsadfasdf"; //字符串 //為了方便輸出,需要將字符串變為字節數組 byte data[] = msg.getBytes(); // 變為了字節數組 for(int x = 0; x < data.length; x++) { output.write(data[x]); //輸出數據 } output.close(); } }
現在發現每當執行完成后之前的內容都被覆蓋了,所以也可以進行數據的追加操作:
范例:追加數據(加入新數據 之前的數據不變 ),第二部加個true
import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class Hello{ public static void main(String[] args) throws Exception { //第一步:定義要輸出文件的File類對象 File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); //輸出信息的時候文件可以不存在,目錄必須存在 if(!file.getParentFile().exists()) { //父路徑不存在 file.getParentFile().mkdirs(); //創建父路徑 } //第二部:利用OutputStream的子類為父類實例化 OutputStream output = new FileOutputStream(file, true); //此處為true,表示追加操作 //第三步:輸出文字信息 String msg = "111adfasdfadsfasdfasdfsadfasdf"; //字符串 //為了方便輸出,需要將字符串變為字節數組 byte data[] = msg.getBytes(); // 變為了字節數組 output.write(data); //輸出數據 output.close(); } }
進行換行操作使用“\r\n”
字節輸入流InputStream
可以實現數據讀取
三個數據讀取方法:
1. 讀取單個字節:public abstract int read() throws IOException //每次執行此方法將讀取單個字節的數據,如果已經去取完成了,沒數據了,那么最后返回的是-1
2. 讀取數據到字節數組中:public int read(byte[]b) throws IOException //每次講數據讀取到數組中,那么會返回一個讀取長度的數據,如果沒有數據,返回長度為-1,要考慮兩種情況:1.要讀取的內容大於開辟數組的內容,長度就是整個數組的長度 2.要讀取的內容小於開辟的數組內容,長度就是全部最后的內容長度,數組裝不滿
3. 讀取 部分內容到字節數組:public int read(byte[]b,int off, int len) throws IOException //每次讀取內容到部分字節數組,只允許讀取滿限制的數組的字節個數,此方法依然返回讀取的長度。
InputStream是抽象類,所以要進行文件的讀取使用FileInputStream子類,子類定義的構造方法:public FileInputStream(File file) throws FileNotFoundException
范例:實現從txt的數據讀取
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; public class Hello{ public static void main(String[] args) throws Exception { //第一步:定義要輸出文件的File類對象 File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); //第二步:實例化InputStream類對象 InputStream input = new FileInputStream(file); //第三步:實現數據讀取操作 byte data[] = new byte[1024]; int len = input.read(data); //將數據讀取到數組中 System.out.println("讀取的內容:【" + new String(data,0,len) +"】"); //第四步:關閉輸入流 input.close(); } }
read()方法可以實現單個字節數據讀取操作,用此方法實現單個字節數據的讀取
范例 :讀取單個字節”
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; public class Hello{ public static void main(String[] args) throws Exception { File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); InputStream input = new FileInputStream(file); byte data[] = new byte[1024]; int foot = 0; //控制保存的角標索引 int temp = 0; //接收每次保存的數據 do { temp = (byte)input.read(); //讀取出來的數據保存到字節數組中 if(temp != -1) { //現在有數據 data[foot ++] = (byte) temp; } } while(temp != -1); //表示后面可能還有數據 System.out.println("讀取的內容:【" + new String(data,0,foot) +"】"); input.close(); } }
以上使用了do...while,實際開發中都用while:
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; public class Hello{ public static void main(String[] args) throws Exception { File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); InputStream input = new FileInputStream(file); byte data[] = new byte[1024]; int foot = 0; //控制保存的角標索引 int temp = 0; //接收每次保存的數據 while((temp = input.read())!= -1 ) { data[foot ++] = (byte)temp; } System.out.println("讀取的內容:【" + new String(data,0,foot) +"】"); input.close(); } }
字符輸出流:Writer
Writer是進行字符輸出操作的抽象類
之所以提供一個Writer類,是因為這個類的輸出方法有一個特別好用的:
輸出字符串:public void write(String str) throws IOException //重點
范例:使用Writer輸出數據
import java.io.File; import java.io.FileWriter; import java.io.Writer; public class Hello{ public static void main(String[] args) throws Exception { File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); if(file.getParentFile().exists()) { file.getParentFile().mkdirs(); } Writer out = new FileWriter(file); String data = "測試數據測試數據測試數據測試數據測試數據" ; out.write(data); //直接輸出字符串 out.close(); } }
若要追加增加數據,之前的不變:
Writer out = new FileWriter(file, true);
雖然Wirter類提供字符數組的輸出操作能力,但本質上講使用Writer類就意味着要執行字符串的直接輸出。
字符輸入流:Reader
數據讀取:public int read(char[] cbuf) throws IOException
范例:讀取數據
import java.io.File; import java.io.FileReader; import java.io.Reader; public class Hello{ public static void main(String[] args) throws Exception { File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); if(file.exists()) { Reader in = new FileReader(file); char data[] = new char[1024]; int len = in.read(data); //向字符數組中保存數據,返回長度 System.out.println(new String(data, 0, len)); in.close(); } } }
Reader與InputStream類相比除了數據類型的差別之外,操作上沒有優勢
字節流與字符流的區別
這兩種流的區別就好比數據庫中的BLOB與CLOB區別
CLOB保存大文本數據,都是字符數據
BLOB保存二進制數據,例如:電影、圖片、文字,都是字節數據
通過任何終端(網絡、文件)讀取或者輸出的數據都一定是字節,但字符是通過內存處理后的數據。
字符輸入:字符輸入的時候字節(磁盤)自動轉換為字符(內存)
字符輸出:字符(內存)自動轉換為字節(磁盤)
在利用字符流輸出的時候,所有的內容實際上都只是輸出到了緩沖區中(內存)。在使用close()關閉的時候會將緩沖區的數據輸出,如果不關閉就不發進行輸出,此時可以利用flush()進行強制刷新
字符使用到了緩沖區,而字節沒有使用到緩沖區。如果處理中文使用字符流,其他任何數據都是用字節流。
綜合案例:文件拷貝
是模擬dos系統中的copy命令完成
編寫一個文件拷貝程序,可實現任意文件拷貝操作,通過初始化參數輸入拷貝的源文件以及拷貝的目標文件路徑,本程序暫不考慮類的設計。
dos拷貝命令:“copy 路徑1 路徑2”
兩種實現思路:
思路一:開辟一個數組,將需拷貝的內容讀取到數組之中,而后一次性輸出到目標路徑中
思路二:采用邊讀邊寫的方式進行拷貝,不是一次性讀取
第一種方式如果文件小沒問題,5M左右。如果文件大,基本內存就被沾滿了,
范例:初期實現
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; public class CopyDemo { public static void main(String[] args) throws Exception { if(args.length != 2) { //參數內容必須是兩個,一個源文件路徑一個目標文件路徑 System.out.println("錯誤的命令,格式為:CopyDemo 源文件路徑 目標文件路徑。"); System.exit(1); //退出程序 } //接下來驗證源文件是否存在 File inFile = new File(args[0]); if(!inFile.exists()) { System.out.println("路徑錯誤,請確定源文件路徑正確。"); System.exit(1); } //如果拷貝的目標文件存在,則也不應該進行拷貝 File outFile = new File(args[1]); if(outFile.exists()) { //目標文件已經存在 System.out.println("拷貝的路徑文件已經存在,請更換路徑。"); System.exit(1); } long start = System.currentTimeMillis(); InputStream in = new FileInputStream(inFile); OutputStream out = new FileOutputStream(outFile); copy(in,out); //開始文件拷貝 in.close(); out.close(); long end = System.currentTimeMillis(); System.out.println("花費的時間:" + (end - start) ); } public static void copy(InputStream input, OutputStream output) throws Exception { int temp = 0; //保存每次讀取的字節量 while((temp = input.read()) != -1 ) { //每次讀取一個字節 output.write(temp); } } }
要先執行以下再輸入路徑。此種方法只能復制很小的文件。
用數組來提升拷貝性能,可以將數據讀取到數組中,而后一次性將數組輸出。
修改拷貝方法:
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; public class CopyDemo { public static void main(String[] args) throws Exception { if(args.length != 2) { //參數內容必須是兩個,一個源文件路徑一個目標文件路徑 System.out.println("錯誤的命令,格式為:CopyDemo 源文件路徑 目標文件路徑。"); System.exit(1); //退出程序 } //接下來驗證源文件是否存在 File inFile = new File(args[0]); if(!inFile.exists()) { System.out.println("路徑錯誤,請確定源文件路徑正確。"); System.exit(1); } //如果拷貝的目標文件存在,則也不應該進行拷貝 File outFile = new File(args[1]); if(outFile.exists()) { //目標文件已經存在 System.out.println("拷貝的路徑文件已經存在,請更換路徑。"); System.exit(1); } long start = System.currentTimeMillis(); InputStream in = new FileInputStream(inFile); OutputStream out = new FileOutputStream(outFile); copy(in,out); //開始文件拷貝 in.close(); out.close(); long end = System.currentTimeMillis(); System.out.println("花費的時間:" + (end - start) ); } public static void copy(InputStream input, OutputStream output) throws Exception { int temp = 0; //保存每次讀取的字節量 byte data[] = new byte[2048]; //數據向數組中讀取 while((temp = input.read(data)) != -1 ) { //每次讀取一個字節 output.write(data, 0 , temp); //輸出數組 } } }
特別快。
對於File、InputStream、OutputStream最直接操作就體現在本程序中