JAVA 筆記:I/O的簡介
風蕭蕭兮易水寒,串流一去兮不復還
I/O 以連接的節點(Node)、傳送的資料型態、以及輸入/輸出來區分
分作以下類別(Class)
檔案 記憶體 Pipe |
X | 位元組(Byte)傳輸 字元(char)傳輸 字串(String)傳輸 |
X |
輸入 輸出 |
通常做位元組傳輸,會叫做Stream
以常見的字串傳輸,會叫做Reader/Writer
舉例來說,FileInputStream是做檔案位元輸入的類別;InputStreamReader是做字串輸入的類別
Pipe是比較特殊的用途,搭配在多執行續上使用,參照PipedInputStream、PipedOutputStream
其他的都直接繼承了InputStream、OutputStream
不論是哪一種傳輸,都是以節點(Node)做為起點或終點,做出Stream這樣的動作
Stream就如同字面上的意思,像水流一樣,有開有關
當Read()或write()方法被呼叫時,串流就會開啟,直到close()被呼叫為止
有時候串流會有保護同時只開放一個程序使用,因此必須記得close()串流,避免其他程序無法使用
在文章的第一行就有提到,串流是無法回頭的
當一直Read()資料時,串流就一點一點的流去,不會回頭
不過如果一定要讓流過的串流再回頭看一次,就要使用一些Buffer的方式,先把串流存起來,就像是蓄水槽一樣
JAVA也有提供一些搭配使用的緩衝區類別,等一下會說到
通常我們會用到的串流,就是以檔案為節點的串流了,讀寫檔案都用的到
使用的方法十分簡單,只需要開檔後,建立起Stream的物件,在使用讀寫的方法就可以了
✎ 記事本 _ ❒ X | 1.在想要的目錄中建立一個文字檔 |
This is a test... |
|
import java.io.*; public class Test { public static void main(String[] args) { FileInputStream fin; try { fin = new FileInputStream("c:\\file.txt"); while (fin.available() > 0) System.out.print(fin.read()); } catch (IOException e) { e.printStackTrace(); } } } |
2.使用程式碼來讀檔 |
◙ C:\Windows\System32\cmd.exe _ ❒ X |
3.執行的結果,很容易吧 |
This is a test... |
前面有提到,FileInputStream他是以Byte為傳輸單位
而英文字母和一般標點符號長度也恰好是一個Byte,因此顯示出來都很正常
不過如果讀寫中文時就會出問題了
✎ 記事本 _ ❒ X | 1.這次的內容是中文 |
中文測試 |
|
◙ C:\Windows\System32\cmd.exe _ ❒ X |
2.執行結果 出現了世界末日的日期 (誤 |
164164164229180250184213 |
如果想要順利的讀取中文,又想堅持使用FileInputStream的話,就要用到Byte[] 來做buffer,而且陣列大小必須是2的倍數,因為中文字佔2個Byte
import java.io.*; public class Test { public static void main(String[] args) { FileInputStream fin; try { fin = new FileInputStream("c:\\file.txt"); byte buf[] = new byte[8];//建立一個大小8Byte的緩衝區 int bufSize;//用來計算讀入的資料大小 while (fin.available() > 0) bufSize = fin.read(buf);//讀取資料到buf內 System.out.print(new String(buf, 0, bufSize)); } catch (IOException e) { e.printStackTrace(); } } } |
1.修改一些程式碼 | |
◙ C:\Windows\System32\cmd.exe _ ❒ X |
2.出現了正確的結果 | |
中文測試 |
不過要是中文字中間夾雜了1Byte大小的字元,讀取就會錯亂,這時候就沒有辦法再堅持使用FileInputStream了
FileReader就登場了!
import java.io.*;
public class Test {
public static void main(String[] args) {
FileReader fin;
try {
fin = new FileReader("c:\\file.txt");
int word;
while (fin.ready()){
word = fin.read();
System.out.print((char)word);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
跟FileInputStream幾乎是一樣的用法
FileReader或FileWriter都是以字元做傳輸的單位,因此不管是中文還是英文,都可以正常的傳輸
到這裡為止,我們知道要如何讀寫檔案文字
但是如果我們要讀寫的是數字、浮點數甚至是物件呢?
當然你可以把123或者0.01還有其他資料用String的方式寫到檔案中,就像剛才那樣
不過如此寫入的話,取出時,還要做字串分析等等繁雜的工作
雖然不是不可以,但是程式就不漂亮了
那麼,該如何把真正的"資料型態"寫到檔案裡面呢?
我們需要一些轉接頭,把資料型態與串流做個連結,稱作chain
為了達到串流連結,我們要用到一些的類別,也就是轉接頭,稱作Filter
Filter除了可以達到轉換型態的功能外,還有些具有緩衝的功能,也就是讓流去的串流可以被回朔
來看看JAVA提供那些Filter的類別
串流緩衝區 | BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter |
物件串流 | ObjectInputStream ObjectOutputStream |
基本資料型態串流 | InputStreamReader InputStreamWriter DataInputStream DataOutputStream |
計算行數 | LineNumberInputStream LineNumberReader |
堆疊串流 | PushbackInputStream PushbackReader |
列印串流 | PrintStream PrintWriter |
這麼多種類轉接頭,看到都暈了…
不過常用的也不會很多,拿比較常用的DataOutputStream舉例吧
DataOutputStream可以將輸出的資料轉換成各種基本資料型態:int, char, double, float...等等
要怎麼轉呢?看看↓
import java.io.*;
public class Test {
public static void main(String[] args) {
FileOutputStream fin;
try {
fin = new FileOutputStream("c:\\file.txt");
DataOutputStream dataWrite = new DataOutputStream(fin);
short sh[] = {1,2,3,4,5};
for(short i : sh){
dataWrite.writeShort(i);//這裡的方法要搭配資料型態使用
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
這樣就完成了寫入short資料型態的數字到檔案裡了,當然也可以用類似方式寫入整數、浮點數等等…
不過當好奇的用記事本打開檔案一看…奇怪,啥都沒有啊!
那是當然的了,因為那不是以"字串"的編碼寫入,而是以你指定的資料編碼寫入(上面例子就是short)
而記事本只會把資料以字串的編碼來呈現,其中字串編碼又分ASCII、UTF…數種
不過當你再寫一段以short讀入資料的程式之後,你就可以直接用short的變數來接收讀入的資料了
超方便的!
剛才有提到,除了基本資料型態之外,物件也可以存進檔案中
不過存進去的只限於"資料",也就是物件中的"屬性(attribute)"
物件中的方法(method)是不會被存的
物件在要被丟到串流中之前,必須先序列化(Serialization),序列化其實也不是甚麼很神祕的東西,只是把資料從物件中抽出來準備傳送而已,這個部分電腦會幫你完成,你只需要在你想要被傳送的類別上,implements Serializable就可以了
用到的Filter類別是ObjectInputStream、ObjectOutputStream,用法跟基本資料型態的一樣,把FileOutputStream的物件掛上去(傳入建構子)就可以了
同樣的,讀取物件時,用該物件來接收,你就可以得到之前存起來的物件了