Java Basics Part 18/20 - Files and I/O
目錄
java.io 包幾乎包含了操作 I/O 所需要的所有類。所有的這些流都代表了一個輸入和一個輸出。java.io 包支持很多的數據類型,包括原始數據類型,對象,本地化字符等等。
Streams
可以認爲流(Streams)就是一串數據序列。有兩種形式:
- 輸入流:輸入流用來從 source 中讀取數據。
- 輸出流:輸出流用來往 destination 中寫入數據。
Java 爲文件和網絡提供了功能非常強大和靈活的支持。但是這裏只介紹基本的流和 I/O。下面逐一介紹最常用的一些流。
Byte Streams
Java 的字節流是用來操作 8-bit 字節的輸入輸出的。雖然有很多類都和字節流有關係,但是最常用的兩個類是 FileInputStream 和 FileOutputStream。下面這個示例演示了把一個文件的內容拷貝到另一個文件中。
import java.io.*;
public class CopyFile {
public static void main(String args[]) throws IOException
{
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("input.txt");
out = new FileOutputStream("output.txt");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
}finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
現在我們在 input.txt 文件中有以下內容:
This is test for copy file.
按照如下步驟編譯然後運行,會得到一個 output.txt 文件,它的內容和 input.txt 是一樣的。
$javac CopyFile.java
$java CopyFile
Character Streams
Java 的字節流用來操作 8-bit 字節的輸入和輸出的,而 Java 的字符流(Character Streams)用來操作 16-bit unicode 的輸入和輸出。雖然有很多類都和字符流有關係,但是最常用的兩個類是 FileReader 和 FileWriter。雖然在底層 FileReader 還是調用的 FileInputStream,FileWriter 還是調用的 FileOutputStream,但是這裏還是有一個很大的差別:
FileReader 一次讀入兩個字節,FileWriter 一次寫入兩個字節
重寫上述的例子,使用字符流來完成文件(內含 unicode 字符)的內容拷貝。
import java.io.*;
public class CopyFile {
public static void main(String args[]) throws IOException
{
FileReader in = null;
FileWriter out = null;
try {
in = new FileReader("input.txt");
out = new FileWriter("output.txt");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
}finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
編譯運行與上一節相同。
Standard Streams
所有的編程語言都提供了標準 I/O,也就是用戶可以從鍵盤讀取輸入並且把輸出顯示到屏幕上。如果接觸過 C/C++ 的話,那麼一定知道三個標準設備 STDIN, STDOUT 和 STDERR。與此相似,Java 也提供瞭如下三個標準流:
- Standard Input: 用來爲程序提供數據,通常鍵盤用作標準輸入流,Java 中用 System.in 表示。
- Standard Output: 用來爲程序輸出數據,通常電腦屏幕用作標準輸出流,Java 中用 System.out 表示。
- Standard Error: 用來爲程序輸出錯誤數據,通常電腦屏幕用作標準錯誤流,Java 中用 System.err 表示。
下面一個簡單的示例,創建了一個 InputStreamReader 用來讀取標準輸入流,直到用戶輸入 “q”:
import java.io.*;
public class ReadConsole {
public static void main(String args[]) throws IOException
{
InputStreamReader cin = null;
try {
cin = new InputStreamReader(System.in);
System.out.println("Enter characters, 'q' to quit.");
char c;
do {
c = (char) cin.read();
System.out.print(c);
} while(c != 'q');
}finally {
if (cin != null) {
cin.close();
}
}
}
}
編譯運行:
$javac ReadConsole.java
$java ReadConsole
Enter characters, 'q' to quit.
1
1
e
e
q
q
Reading and Writing Files
如前所述,流是一串數據序列。InputStream 用來從 source 中讀取數據, OutputStream 往 destination 中寫入數據。
下圖是 Input 和 Output 流的類結構圖。
兩個重要的流是 FileInputStream 和 FileOutputStream,下面就來介紹下這兩個類。
FileInputStream
這個流用來從文件中讀取數據。這個類有很多的構造器。
如下構造器接收一個字符串類型的形參作爲文件名,並且基於這個文件創建一個輸入流:
InputStream f = new FileInputStream("C:/java/hello");
如下這個構造器結構一個 File 對象,基於這個文件創建一個輸入流,首先要使用 File() 創建一個 file 對象:
File f = new File("C:/java/hello");
InputStream f = new FileInputStream(f);
一旦持有了 InputStream,那麼就有很多操作 流 的方法可以使用了。
SN | Methods with Description |
---|---|
1 | public void close() throws IOException{}: 關閉資源。 |
2 | protected void finalize()throws IOException {}:清除 文件連接。 |
3 | public int read()throws IOException{}:讀取 1 個字節的數據。 |
4 | public int read(byte[] r) throws IOException{}:讀取 r.length 個字節的數據到數組裏。 |
5 | public int available() throws IOException{}:返回文件輸入流中還有多少字節數據可讀。 |
還有其他一些很重要的輸入流,詳情請點擊:
FileOutputStream
FileOutputStream 創建一個可以寫入數據的文件。如果文件不存在,會創建一個。
下面是它的兩個構造器。
第一個構造器接收一個字符串類型的文件名稱做參數,基於這個文件創建一個輸出流:
OutputStream f = new FileOutputStream("C:/java/hello")
第二個構造器接收一個文件對象做參數,然後寫入這個文件對象指向的文件:
File f = new File("C:/java/hello");
OutputStream f = new FileOutputStream(f);
一旦持有了 OutputStream,那麼就有很多操作流的方法可以使用了。
SN | Methods with Description |
---|---|
1 | public void close() throws IOException{}: 關閉流 |
2 | protected void finalize()throws IOException {}:確保清除文件鏈接 |
3 | public void write()throws IOException{}:寫入一個字節 |
4 | public void write(byte[] w):寫入整個字節數組 |
還有其他的輸出流可供使用,詳情請點擊:
舉例:
演示 InputStream 和 OutputStream
import java.io.*;
public class fileStreamTest{
public static void main(String args[]){
try{
byte bWrite [] = {11,21,3,40,5};
OutputStream os = new FileOutputStream("test.txt");
for(int x=0; x < bWrite.length ; x++){
os.write( bWrite[x] ); // writes the bytes
}
os.close();
InputStream is = new FileInputStream("test.txt");
int size = is.available();
for(int i=0; i< size; i++){
System.out.print((char)is.read() + " ");
}
is.close();
}catch(IOException e){
System.out.print("Exception");
}
}
}
上面這個例子創建出 test.txt 文件,然後以二進制格式寫入一串給定的數字。相同的內容會出現在 stdout 上。
File Navigation and I/O
還有一些其他的操作 File 的類可以學習:
Directories in Java
可以使用 File 對象來 創建目錄, 列出目錄中的文件 等等。
Creating Directories
有兩個很有用的 File 工具方法,都可以用來創建目錄:
- mkdir():返回 boolean。如果 路經 存在,那麼返回失敗。
- mkdirs():遞歸創建文件夾
下面這個實例創建了 “/tmp/user/java/bin” 目錄
import java.io.File;
public class CreateDir {
public static void main(String args[]) {
String dirname = "/tmp/user/java/bin";
File d = new File(dirname);
// Create directory now.
d.mkdirs();
}
}
注意路徑分隔符,Unix 和 Windows 都可以處理 “/”
Listing Directories
可以使用 File 對象的 list() 方法來列出文件夾中的所有文件:
import java.io.File;
public class ReadDir {
public static void main(String[] args) {
File file = null;
String[] paths;
try{
// create new file object
file = new File("/tmp");
// array of files and directory
paths = file.list();
// for each name in the path array
for(String path:paths)
{
// prints filename and directory name
System.out.println(path);
}
}catch(Exception e){
// if any error occurs
e.printStackTrace();
}
}
}
這個程序會列出 /tmp 文件夾中的所有目錄和文件:
test1.txt
test2.txt
ReadDir.java
ReadDir.class