Java實現檔案寫入——IO流(輸入輸出流詳解)
輸入輸出的重要性:
輸入和輸出功能是Java對程式處理資料能力的提高,Java以流的形式處理資料。流是一組有序的資料序列,根據操作的型別,分為輸入流和輸出流。
程式從輸入流讀取資料,向輸出流寫入資料。Java是面向物件的程式語言,每一個數據流都是一個物件,它們提供了各種支援“讀入”與“寫入”操作的流類。
Java的輸入輸出功能來自java.io 包中的InputStream類、OutputStream類、Reader類和Writer類以及繼承它們的各種子類。
(一)解析檔案處理的奧祕
1、學習使用檔案類 : File類
File類用於封裝系統的檔案和目錄的相關資訊。在該類中定義了一些與平臺無關的方法來操作檔案。例如檔案的大小、修改時間、檔案路徑等。
建立 File 物件可以通過下面3種方式:
方法1: | 方法2: | 方法3: |
---|---|---|
new File(String pathName) | File file = new File(“E://1.txt”) | new File(String parent , String child) |
parent :父抽象路徑名;child:子路徑名字串 |
2、如何獲取檔案資訊
File 類是對檔案和資料夾的抽象,包含了對檔案和資料夾的多種屬性和操作方法。File類的常用方法如下表:
返回 | 方法 | 說明 |
---|---|---|
String | getName | 獲取檔名稱 |
String | getParent | 獲取檔案的父路徑字串 |
String | getPath | 獲取檔案的相對路徑字串 |
String | getAbsolutePath | 獲取檔案的絕對路徑字串 |
boolean | exists | 判斷檔案或者資料夾是否存在 |
boolean | isFile | 判斷是不是檔案型別 |
boolean | isDirectory | 判斷是不是資料夾型別 |
boolean | delete | 刪除檔案或資料夾,如果刪除成功返回結果為true |
boolean | mkdir | 建立資料夾,建立成功返回true |
boolean | setReadOnly | 設定檔案或資料夾的只讀屬性 |
long | length | 獲取檔案的長度 |
long | lastModified | 獲取檔案的最後修改時間 |
String[ ] | list | 獲取資料夾中的檔案和子資料夾的名稱,並存放到字串陣列中 |
下面通過例項介紹File類獲取檔案資訊
package com.zch.io;
import java.io.File;
import java.util.Date;
/**
* 在src根目錄下建立FileInfo類,在該類的主方法中建立檔案物件,通過File類的相關方法,獲取檔案的相關資訊
*
* @author zch
*
*/
public class FileInfo {
public static void main(String[] args) {
String filePath = "src/com/zch/io/FileInfo.java";
// 根據指定路徑建立檔案物件
File file = new File(filePath);
System.out.println("檔名稱:" + file.getName());
System.out.println("檔案是否存在:" + file.exists());
System.out.println("檔案的相對路徑:" + file.getPath());
System.out.println("檔案的絕對路徑:" + file.getAbsolutePath());
System.out.println("是否為可執行檔案:" + file.canExecute());
System.out.println("檔案可以讀取:" + file.canRead());
System.out.println("檔案可以寫入:" + file.canWrite());
System.out.println("檔案上級路徑:" + file.getParent());
System.out.println("檔案大小:" + file.length() + "B");
System.out.println("檔案最後修改時間:" + new Date(file.lastModified()));
System.out.println("是否檔案型別:" + file.isFile());
System.out.println("是否為資料夾:" + file.isDirectory());
}
}
執行結果如下:
檔名稱:FileInfo.java
檔案是否存在:true
檔案的相對路徑:src\com\zch\io\FileInfo.java
檔案的絕對路徑:D:\Java\IO\src\com\zch\io\FileInfo.java
是否為可執行檔案:true
檔案可以讀取:true
檔案可以寫入:true
檔案上級路徑:src\com\zch\io
檔案大小:1195B
檔案最後修改時間:Sat Sep 09 21:30:10 CST 2017
是否檔案型別:true
是否為資料夾:false
在使用delete()方法刪除File物件時,如果刪除的物件是目錄,該目錄中的內容必須為空。
(二)使用位元組輸入輸出流
位元組流用於處理二進位制資料的讀取和寫入,它以位元組為單位,InputStream類和OutputStream類是位元組流的抽象類,它們定義了資料流讀取和寫入的基本方法。各個子類會依其特點實現或覆蓋這些方法。
1、位元組數入流抽象類InputStream
InputStream 類是位元組輸入流的抽象類,定義了操作輸入流的各種方法,這些方法如表:
返回 | 方法 | 說明 |
---|---|---|
int | available() | 返回當前輸入流的資料讀取方法可以讀取的有效位元組數量 |
Abstract int | read() | 從當前資料流中讀取一個位元組。若已達到流結尾,則返回-1 |
int | read(byte[ ] bytes) | 從當前輸入流讀取一定的byte資料,並存取在陣列中,然後返回讀取的byte資料的數量,若已到達流結尾,則返回-1。 |
void | reset() | 將當前的輸入流重新定位到最後一次呼叫mark()方法時的位置 |
void | mark(int readlimit) | 在當前輸入流中做標記位置,當呼叫reset()方法時將返回到該位置,從標記位置開始,到再讀入readlimit個字元為止,這個標記都維持有效。 |
Boolean | markSupported() | 測試當前輸入流是否支援mark()和reset()方法,只要其中一個不支援,則返回false |
long | skip(long n) | 跳過和丟棄當前輸入的n個位元組資料 |
void | close() | 關閉當前輸入流,並釋放任何與之相關聯的系統資源 |
InputStream 類是抽象類,不能通過new關鍵字來建立該例項物件,需要其子類建立該例項物件。下面通過例項如何使用InputStream從控制檯獲取使用者輸入的資料資訊。
package com.zch.io;
import java.io.IOException;
import java.io.InputStream;
/**
* 建立InputStream例項inp,並將其賦值為System類的in屬性,定義為控制檯輸入流,從inp輸入流中獲取位元組資訊,
* 用這些位元組資訊建立字串,並將其在控制檯上輸出。
* @author zch
*
*/
public class InputMessage {
public static void main(String[] args) {
InputStream inp = System.in;
byte[] bytes = new byte[1024];
try {
while(inp.read() != -1){
//根據使用者輸入的資訊建立字串
String str = new String(bytes).trim();
}
inp.close(); //關閉流
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2、位元組輸出流抽象類OutputStream類
OutputStream定義了輸出流的各種方法,如下表:
返回 | 方法 | 說明 |
---|---|---|
void | write(byte[ ] b) | 將byte[ ] 陣列中的資料寫入當前輸出流 |
void | write(byte[] b ,int off, int len) | 將byte[ ]陣列下標off開始的len長度的資料寫入當前輸出流 |
Abstract void | write(int b) | 寫入一個byte資料到當前輸出流 |
void | flush() | 重新整理當前輸出流,並強制寫入所有緩衝的位元組資料 |
void | close() | 關閉當前輸出流 |
和InputStream類一樣,OutputStream 類是抽象類,不能通過new關鍵字來建立該例項物件,需要其子類建立該例項物件。
package com.zch.io;
import java.io.IOException;
import java.io.OutputStream;
/**
* 建立OutputStream例項out,並將其賦值為System.out標準輸出流。通過write()方法向流寫入資料。
* @author zch
*
*/
public class OutputData {
public static void main(String[] args) {
OutputStream output = System.out; //例項化OutputStream物件
byte[] bytes = "使用OutputStream輸出流在控制檯輸出字串\n".getBytes(); //建立bytes陣列
try {
output.write(bytes);
bytes = "輸出內容:\n".getBytes();
output.write(bytes); //向流中寫入資料
bytes = "Java資料互動管道——IO流 \n".getBytes();
output.write(bytes);
output.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
輸出結果如下:
使用OutputStream輸出流在控制檯輸出字串
輸出內容:
Java資料互動管道——IO流
3、檔案位元組輸入流類 : FileInputStream類
檔案位元組輸入流可以從指定路徑的檔案中讀取位元組資料。檔案位元組輸入流類繼承InputStream類,並實現了讀取輸入流的各種方法。
建立檔案位元組輸入流建立的構造方法語法如下:
- 語法1:以File物件為引數建立FileInputStream例項
new FileInputStream(File file)
- 語法2:以檔案物件的絕對路徑為引數建立FIleInputStream例項
new FileInputStream(String filepath)
4、檔案位元組輸出流類:FileOutputStream
檔案位元組輸出流關聯指定檔案路徑的檔案,資料通過檔案位元組輸出流以位元組為單位輸出並儲存到檔案中。檔案位元組輸出流繼承自OutputStream類,並實現OutputStream類的各種方法。
檔案位元組輸出流的構造方法語法如下:
- 語法1:以File物件為引數建立FileOutputStream例項
new FileOutputStream(File file)
- 語法2:以檔案物件的絕對路徑為引數建立FIleOutputStream例項
new FileOutputStream(String filepath)
下面通過例項介紹檔案的寫入和讀取:
package com.zch.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 建立OutputStream例項out,並將其賦值為System.out標準輸出流,通過write方法向流中寫入資料
*
* @author zch
*
*/
public class FileCreate {
public static void main(String[] args) {
File file = new File("D:/", "word.txt"); //建立檔案物件
try {
if (!file.exists()) { //如果檔案不存在則新建檔案
file.createNewFile();
}
FileOutputStream output = new FileOutputStream(file);
byte[] bytes = "Java資料交流管道——IO流".getBytes();
output.write(bytes); //將陣列的資訊寫入檔案中
output.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
FileInputStream input = new FileInputStream(file);
byte[] bytes2 = new byte[1024];
int len = input.read(bytes2);
System.out.println("檔案中的資訊是:" + new String(bytes2, 0, len));
input.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(三) 使用字元輸入輸出流
字元輸入輸出流 與 位元組輸入輸出流有相同的功能,但傳送資料的方式不一樣,位元組流以位元組為單位傳送資料,可以使任何型別的資料,例如文字、音訊、視訊、圖片等。字元流以字元為單位傳送資料,只能傳送文字型別的資料。使用字元輸入輸出流的好處是,當讀取中文時不會出現亂碼問題,而使用位元組輸入輸出流時,卻不能保證這一點。
1、字元輸入流抽象類:Reader類
該類定義了操作字元輸入流的方法,如下表:
返回 | 方法 | 說明 |
---|---|---|
boolean | ready() | 判斷此資料流是否準備好 |
int | read() | 讀入一個字元,若已讀到流結尾,則返回值為-1 |
int | read(char[ ]) | 讀取一些字元到char[ ]陣列內,並返回所讀入的字元的數量,若已到達流結尾,則返回-1 |
Abscract int | read(char[ ] chars,int off,int len) | 讀取一些字元到char[ ]陣列下標從off開始到off+len的位置,並返回所讀入的字元的數量,若已到達流結尾,則返回-1; |
void | reset() | 將當前輸入流重新定位到最後一次mark()方法時的位置 |
void | mark(int readLimit) | 將當前輸入流中做標記,當呼叫reset方法時將返回到該位置,從標記位置開始,到再讀入readLimit個字元為止,這個標記都維持有效 |
boolean | markSupported | 測試當前輸入流是否支援mark()方法和reset()方法。只要有一個方法不支援,則返回-1 |
long | skip(long n) | 跳過引數n指定的字元數量,並返回所跳過字元的數量 |
Abstract void | close() | 關閉字元輸入流,並釋放與之關聯的所有資源 |
2、字元輸出流類Writer類
Writer 類主要是用於解決字元輸入流的類,其地位與Reader類在輸入流的地位和作用是相同的,也是所有字元輸出流的流類。
Writer類的主要方法如下:
返回 | 方法 | 說明 |
void | write(char[ ] cbuf) | 將字元陣列的資料寫入字元輸出流 |
Abstract void | write(char[ ] cbuf int off ,int len) | 將字元陣列從下標off 開始向輸入流寫入長度為len的資料 |
void | write(int c ) | 向字元輸入流中寫入一個字元資料 |
void | write(String str ) | 向輸入流中寫入一個字串資料 |
void | write(String str , int off ,int len) | 向輸入流中寫入一個字串從off 開始長度為len的資料 |
Abstract void | flush() | 重新整理當前輸出流,並強制寫入所有緩衝區的位元組資料 |
void | close() | 向輸出流中寫入緩衝區的資料,然後關閉當前輸出流,釋放所有與當前輸出流相關聯的系統資源 |
3、檔案字元輸入流FileReader
檔案字元輸入流與檔案位元組輸入流的功能相似,但是傳送資料的方式不一樣,位元組流以位元組為單位傳送資料,可以使文字、視訊、音訊、圖片等。字元流以字元為單位傳送資料,只能傳送文字型別的資料。
建立字元輸入流常用的構造方法:
- 語法1:
new FileReader(File file);
- 語法2:
new FileReader(String path);
下面通過例項介紹FileReader類讀取指定磁碟檔案的內容。
package com.zch.io;
import java.io.File;
import java.io.FileReader;
public class FileInAndOut {
public static void main(String[] args) {
//定義指定磁碟的檔案的File物件
File file = new File("D://word.txt");
if(! file.exists()){
System.out.println("對不起,不包含指定路徑的檔案");
}else{
//根據指定路徑的File物件建立FileReader物件
try {
FileReader fr = new FileReader(file);
char[] data = new char[23]; //定義char陣列
int length = 0;
while((length = fr.read(data))>0){ //迴圈讀取檔案中的資料
String str = new String(data,0,length); //根據讀取檔案的內容建立String 物件
System.out.println(str); //輸出讀取內容
}
fr.close(); //關閉流
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
執行結果如下圖:
4、檔案字元輸出流FileWriter
檔案字元輸出流繼承自Writer類,提供了向檔案輸出的各種方法,資料通過檔案字元輸出流以字元為單位輸出並儲存到檔案中。
package com.zch.io;
/**
* 通過給定的String型別引數的指定檔名稱與路徑,建立FileWriter類。
*
* @author zch
*/
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo {
public static void main(String[] args) {
File file = new File("D://word2.txt"); //建立指定檔案
try {
if(! file.exists()){
file.createNewFile(); //如果指定檔案不存在,新建檔案
}
FileReader fr = new FileReader("D://word.txt");
FileWriter fw = new FileWriter(file); //建立FileWriter物件
int length = 0;
while((length = fr.read()) != -1){ //如果沒有讀到檔案末尾
fw.write(length); //向檔案寫入資料
}
fr.close(); //關閉流
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
執行後建立了Word2.txt 檔案,並向其中寫入資料
(四)IO流實戰:
1、Java IO流實現複製資料夾
通過IO不僅可以複製檔案,還可以複製資料夾,但是資料夾內,可能包含其他資料夾,因此需要對他們進行分別複製。
package com.zch.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class CopyFile {
public static void main(String[] args) {
File sourceFile = null;
File desFile = null;
String sourceFolder = "D://簡歷2";
String copyFolder = "D://copy";
sourceFile = new File(sourceFolder);
if (!sourceFile.isDirectory() || !sourceFile.exists()) {
System.out.println("原始檔夾不存在!");
} else {
desFile = new File(copyFolder);
desFile.mkdir();
copy(sourceFile.listFiles(), desFile);
System.out.println("資料夾複製成功!");
}
}
/**
* 建立copy方法,該方法接收檔案陣列和目標資料夾兩個引數,如果目標資料夾不存在,則呼叫mkdir()方法建立資料夾,然後再迴圈中將檔案陣列
* 中的每個檔案物件寫到目標資料夾內。
* @param fl
* @param file
*/
public static void copy(File[] fl, File file) {
if (!file.exists()) { // 如果資料夾不存在
file.mkdir(); // 建立新的資料夾
}
for (int i = 0; i < fl.length; i++) {
if (fl[i].isFile()) { // 如果是檔案型別,則複製檔案
try {
FileInputStream fis = new FileInputStream(fl[i]);
FileOutputStream out = new FileOutputStream(new File(
file.getPath() + File.separator + fl[i].getName()));
int count = fis.available();
byte[] data = new byte[count];
if ((fis.read(data)) != -1) {
out.write(data);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (fl[i].isDirectory()) { // 如果是資料夾型別
File des = new File(file.getPath() + File.separator
+ fl[i].getName());
des.mkdir(); // 在目標資料夾中建立相同的資料夾
copy(fl[i].listFiles(), des); // 遞迴呼叫方法本身
}
}
}
}
執行本例項,會將D盤中的簡歷檔案中的內容複製到D盤的copy資料夾中,而且包含資料夾的子資料夾
2、Java IO流實現分行向檔案中寫入資料
FileWriter類可以向檔案寫入字元資料,如果將FileWriter類封裝到BufferWriter類的緩衝字元流中,能夠實現緩衝字元輸出流,並且可以通過讀輸出流的newLine()方法,來實現資料的分行寫入。
package com.zch.io;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/**
* 建立BranchWriter類,在主方法中定義檔案物件,將該物件作為引數建立BufferedWriter類例項,
* 呼叫該例項的writer方法將資料寫入檔案中,然後 呼叫newLine()方法寫入換行符,實現分行向檔案寫入資料。
*
* @author zch
*
*/
public class BranchWriter {
public static void main(String[] args) {
String filePath = "D://BranchWriter.txt";
File file = new File(filePath);
try {
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file); // 建立檔案輸出流
BufferedWriter bw = new BufferedWriter(fw); // 使用緩衝區資料流封裝輸出流
for (int i = 0; i < 100; i++) { //迴圈寫入100行資料
bw.write("Java互動管道——IO流".toCharArray());// 寫入資料到輸出流
bw.newLine(); // 寫入換行符
bw.flush(); // 重新整理緩衝區
}
System.out.println("成功寫入資料!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
3、刪除指定檔案
File類的delete()方法可以實現刪除指定的檔案,首先使用目標檔案路徑建立File類的例項物件,然後再呼叫File類的delete()方法。
package com.zch.io;
import java.io.File;
public class FileDelete {
public static void main(String[] args) {
String filePath = "D://word.txt";
File file = new File(filePath);
delFile(file);
}
public static void delFile(File file) {
if (!file.exists()) {
System.out.println("檔案不存在!");
return;
}
boolean rs = file.delete();
if (rs) {
System.out.println("檔案刪除成功!");
} else {
System.out.println("檔案刪除失敗!");
}
}
}