11.3.1.3 讀取文件
雖然前面介紹了流的概念,但是這個概念對於初學者來說,還是比較抽象的,下面以實際的讀取文件為例子,介紹流的概念,以及輸入流的基本使用。
按照前面介紹的知識,將文件中的數據讀入程序,是將程序外部的數據傳入程序中,應該使用輸入流——InputStream或Reader。而由於讀取的是特定的數據源——文件,則可以使用輸入對應的子類FileInputStream或FileReader實現。
在實際書寫代碼時,需要首先熟悉讀取文件在程序中實現的過程。在Java語言的IO編程中,讀取文件是分兩個步驟:1、將文件中的數據轉換為流,2、讀取流內部的數據。其中第一個步驟由系統完成,只需要創建對應的流對象即可,對象創建完成以後步驟1就完成了,第二個步驟使用輸入流對象中的read方法即可實現了。
使用輸入流進行編程時,代碼一般分為3個部分:1、創建流對象,2、讀取流對象內部的數據,3、關閉流對象。下面以讀取文件的代碼示例:
import java.io.*;
/**
* 使用FileInputStream讀取文件
*/
public class ReadFile1 {
public static void main(String[] args) {
//聲明流對象
FileInputStream fis = null;
try{
//創建流對象
fis = new FileInputStream("e:\\a.txt");
//讀取數據,並將讀取到的數據存儲到數組中
byte[] data = new byte[1024]; //數據存儲的數組
int i = 0; //當前下標
//讀取流中的第一個字節數據
int n = fis.read();
//依次讀取後續的數據
while(n != -1){ //未到達流的末尾
//將有效數據存儲到數組中
data[i] = (byte)n;
//下標增加
i++;
//讀取下一個字節的數據
n = fis.read();
}
//解析數據
String s = new String(data,0,i);
//輸出字符串
System.out.println(s);
}catch(Exception e){
e.printStackTrace();
}finally{
try{
//關閉流,釋放資源
fis.close();
}catch(Exception e){}
}
}
}
在該示例代碼中,首先創建一個FileInputStream類型的對象fis:
fis = new FileInputStream("e:\\a.txt");
這樣建立了一個連接到數據源e:\a.txt的流,並將該數據源中的數據轉換為流對象fis,以後程序讀取數據源中的數據,只需要從流對象fis中讀取即可。
讀取流fis中的數據,需要使用read方法,該方法是從InputStream類中繼承過來的方法,該方法的作用是每次讀取流中的一個字節,如果需要讀取流中的所有數據,需要使用循環讀取,當到達流的末尾時,read方法的返回值是-1。
在該示例中,首先讀取流中的第一個字節:
int n = fis.read();
並將讀取的值賦值給int值n,如果流fis為空,則n的值是-1,否則n中的最後一個字節包含的時流fis中的第一個字節,該字節被讀取以後,將被從流fis中刪除。
然後循環讀取流中的其它數據,如果讀取到的數據不是-1,則將已經讀取到的數據n強制轉換為byte,即取n中的有效數據——最後一個字節,並存儲到數組data中,然後調用流對象fis中的read方法繼續讀取流中的下一個字節的數據。一直這樣循環下去,直到讀取到的數據是-1,也就是讀取到流的末尾則循環結束。
這裡的數組長度是1024,所以要求流中的數據長度不能超過1024,所以該示例代碼在這裡具有一定的局限性。如果流的數據個數比較多,則可以將1024擴大到合適的個數即可。
經過上面的循環以後,就可以將流中的數據依次存儲到data數組中,存儲到data數組中有效數據的個數是i個,即循環次數。
其實截至到這裡,IO操作中的讀取數據已經完成,然後再按照數據源中的數據格式,這裡是文件的格式,解析讀取出的byte數組即可。
該示例代碼中的解析,只是將從流對象中讀取到的有效的數據,也就是data數組中的前n個數據,轉換為字符串,然後進行輸出。
在該示例代碼中,只是在catch語句中輸出異常的信息,便於代碼的調試,在實際的程序中,需要根據情況進行一定的邏輯處理,例如給出提示信息等。
最後在finally語句塊中,關閉流對象fis,釋放流對象占用的資源,關閉數據源,實現流操作的結束工作。
上面詳細介紹了讀取文件的過程,其實在實際讀取流數據時,還可以使用其它的read方法,下面的示例代碼是使用另外一個read方法實現讀取的代碼:
import java.io.FileInputStream;
/**
* 使用FileInputStream讀取文件
*/
public class ReadFile2 {
public static void main(String[] args) {
//聲明流對象
FileInputStream fis = null;
try{
//創建流對象
fis = new FileInputStream("e:\\a.txt");
//讀取數據,並將讀取到的數據存儲到數組中
byte[] data = new byte[1024]; //數據存儲的數組
int i = fis.read(data);
//解析數據
String s = new String(data,0,i);
//輸出字符串
System.out.println(s);
}catch(Exception e){
e.printStackTrace();
}finally{
try{
//關閉流,釋放資源
fis.close();
}catch(Exception e){}
}
}
}
該示例代碼中,只使用一行代碼:
int i = fis.read(data);
就實現了將流對象fis中的數據讀取到字節數組data中。該行代碼的作用是將fis流中的數據讀取出來,並依次存儲到數組data中,返回值為實際讀取的有效數據的個數。
使用該中方式在進行讀取時,可以簡化讀取的代碼。
當然,在讀取文件時,也可以使用Reader類的子類FileReader進行實現,在編寫代碼時,只需要將上面示例代碼中的byte數組替換成char數組即可。
使用FileReader讀取文件時,是按照char為單位進行讀取的,所以更適合於文本文件的讀取,而對於二進制文件或自定義格式的文件來說,還是使用FileInputStream進行讀取,方便對於讀取到的數據進行解析和操作。
讀取其它數據源的操作和讀取文件類似,最大的區別在於建立流對象時選擇的類不同,而流對象一旦建立,則基本的讀取方法是一樣,如果只使用最基本的read方法進行讀取,則使用基本上是一致的。這也是IO類設計的初衷,使得對於流對象的操作保持一致,簡化IO類使用的難度。