Java輸入與輸出(下)
作者:資深工程師 段維瀚先生 |
上期介紹了基本的 I/O 觀念,以及 non-stream I/O 之後,本期要介紹 Java I/O Stream。篇幅較長,因為作者在每一個細節都講解得非常詳盡,相信各位可以從中獲益良多。 Java I/O Stream(串流)
Stream是串流的意思,Data Stream可翻譯成資料串流,亦即資料在資料來源端(Data Source)與資料目的端(Data Sink)之間流動的概念。就像溪流有著固定的源頭、流向地及水流方向。流動的水就好比資料,水的源頭是資料來源端(Data Source),水所流向的目的地就是資料目的端(Data Sink),資料傳輸則是具方向性的。每一條溪流的源頭與目的地都不同(例如目的地不一定是大海,有可能是湖泊或是其他溪流的分支…等),因此Data Stream也會有不同的來源端(Data Source)與目的端(Data Sink)。
1. Source & Sink Stream
Data Source Stream又稱為Input stream或Reader,用途是初始資料流。Data Source Sink又稱為Output stream或Writer,作用為接收資料流。Java支援二種Stream資料型態分別是byte與character。當傳輸的資料是Byte時,使用Input stream類別與Output stream類別;傳輸的資料若為Character,則用Reader類別與Writer類別。
| Byte Streams | Character Streams | Source Streams |
InputStream | Reader | Sink Streams | OutputStream |
Writer |
|

【圖】Data Stream、Data Source與Data Sink之間的關係圖
‧Input Stream與Output Stream
Input Stream與Output Stream用來輸入與輸出byte資料串列(byte stream),Input Stream輸入標的可以是File、String、bytes array、pipe、internet與其他的stream;Output Stream的輸出標的則包括File、bytes array、pipe。InputStream類別與OutputStream類別分別繼承自Object。
InputStream以read()方法讀取單一byte資料或一連串bytes(a bytes array)資料。 方法名稱 |
傳回值 | 說明 | read() | int |
從InputStream物件中讀取一個byte,並傳回下一組位元組資料。。 | read(byte[] b) | static File | 將從InputStream物件中讀取某些數量的資料填到b中,並傳回已經讀取的位元組數目。 |
read(byte[] b, int off, int len) | static File | 在InputStream物件中從off位置開始讀取len個位元組資料並填入到b中,再傳回已經讀取的位元組數目。 |
|
read()方法的傳回值若為-1,表示已到檔尾。利用read()方法可讀取InputStream物件的資料,但是InputStream物件無法做寫入的動作。
OutputStream以write()方法寫入單一byte資料或一連串bytes(a bytes array)的資料。
方法名稱 |
傳回值 | 說明 | write(byte[ ] b) | void |
將b中所有的資料寫入。 | write(byte[ ] b, int off, int len) | void | 將b中off位置開始的len個位元組的資料寫入。 |
write(int b) | void | 寫入一個指定的byte(也就是b的位元組資料)。 |
|
利用write()方法可寫入OurputStream物件的資料,但OutputStream物件無法做讀取的動作。 ‧Reader與Writer
Reader與Writer用來輸入與輸出character資料串列(character stream)。Reader類別與Writer類別也分別繼承自Object而來。
Read與Write關於讀取與寫入的方法:
Read讀取資料的方法是read()方法,可用來讀取單一character資料或者是一連串character(String)的資料。read()方法的傳回值若為-1,表示已到檔尾。 方法名稱 |
傳回值 | 說明 | read() | int |
從Reader物件中讀取一個字元,並傳回下一個字元。 | read(char[] cbuf) | int | 從Reader物件中讀取些許字元填入cbuf中,並傳回已經讀取的字元數目。 |
read(char[] cbuf) | int | 在Reader物件中從off位置開始讀取len個字元資料並填入到cbuf中,並傳回已經讀取的字元數目。 |
Write寫出資料的方法是write()方法,可用來寫出單一character資料或者是一連串character(String)的資料。
方法名稱 | 傳回值 | 說明 | write(char[] cbuf) |
void | 寫出一個名為cbuf的字元陣列cbuf。 | void write(char[] cbuf, int off, int len | void | 將cbuf中從第off位置開始,並寫出長度為len的字元資料 |
write(int c) | void | 寫出一個指定的字元(也就是c的16位元所表示的字元資料)。 | write(String str) |
void | 寫出一個字串(str)。 | write(String str, int off, int len) | void | 將str中從第off位置開始,並寫出長度為len的字元資料。 |
2. Node Streams
node Streams是可直接連結到實體資源的Stream物件,所謂實體資源是指Files、memory與Pipe。
Node Streams列表
Byte Streams | Character Streams | 用途 | FileInputStream
FileOutputStream | FileReader
FileWriter | 讀取/寫入檔案中的資料 | ByteArrayInputStream
ByteArrayOutputStream | CharArrayReader
CharArrayWriter | 讀取記憶體中buffer內的資料/將資料寫入記憶體中的buffer(Array資料型別) | |
StringReader
StringWriter | 讀取記憶體中buffer內的資料/將資料寫入記憶體中的buffer(String資料型別) | PipedInputStream
PipedOutputStream |
PipedReader
PipedWriter | Pipe(process或thread)。利用Piped Stream能讓執行緒(threads)之間能彼此溝通,在使用上的時候必需要成對使用(有input端也有output端)。 |
‧FileInputStream與FileOutputStream
利用FileInputStream與FileOutputStream分別繼承自InputStream與OutputStream類別,在執行上是位元導向(byte-oriented),是用來開啟檔案以供讀取檔案內容,若檔案不存在時則會自動建立新檔。
FileInputStream建構子
FileInputStream建構子 | 說明 |
FileInputStream(File file) | 建立一個FileInputStream並指定一個File類別物件file。 | FileInputStream(FileDescriptor fdObj) | 建立一個FileInputStream並指定一個FileDescriptor類別物件fdObj。 |
FileInputStream(String name) | 建立一個FileInputStream並給定檔案名稱name |
FileOutputStream建構子
FileInputStream建構子 | 說明 | FileInputStream(File file) | 建立一個FileInputStream並指定一個File類別物件file。 |
FileOutputStream(File file, Boolean append) | 建立一個FileOutputStream並指定一個File類別物件file,若append變數設定為true則會將欲加入的新文字接續在原始檔案中繼有文字之後(write to the end of the file),反之若設為false則會將原始檔案中的文字清除並以新增加的文字取代。 | FileOutputStream(FileDescriptor fdObj) | 建立一個FileIOututStream並指定一個FileDescriptor類別物件fdObj |
FileOutputStream(String name) | 建立一個FileOutputStream並給定檔案名稱name | FileOutputStream(String name, boolean append) | 建立一個FileOutputStream並給定檔案名稱name,若append變數設定為true則會將欲加入的新文字接續在原始檔案中繼有文字之後(write to the end of the file),反之若設為false則會將原始檔案中的文字清除並以新增加的文字取代。 |
FileOutputStream範例實作
利用FileOutputStream將資料寫到檔案中 |
1. import java.io.*;
2. public class Ex_FileOutputStream 3. { 4. public static void main(String[] args) throws Exception
5. { 6. String s; 7. s = "Java輸入與輸出(Java.io套裝模組)"; 8. byte[] newData = s.getBytes(); 9. System.out.println("將字串\"" + s + "\"寫到檔案”)
10. System.out.println("資料長度:" + newData.length + " bytes."); 11. FileOutputStream fo = new FileOutputStream("SampleFile2.txt", true); 12. fo.write(newData); //直接將byte[]寫入檔案
13. fo.close(); // 關閉檔案 14. } 15. } | 執行結果:
將字串"Java輸入與輸出(Java.io套裝模組)"寫到檔案
資料長度:31 bytes.
資料寫入前

資料寫入後 
|
程式第7行利用getBytes()方法將s字串指派到newData變數(byte[]陣列)中。第11行利用FileOutputStream開啟SampleFile2.txt檔,並將第二個參數設定為True,使後來加入新增資料時可保存先前的資料不至於被覆蓋。第12行利用write()方法直接將newData寫入。 ‧FileReader與FileWriter
FileReader與FileWriter分別是InputStreamReader與OutputStreamWriter的子類別(sub-classes)。FileReader因繼承自InputStreamReader類別,所以能合法使用InputStreamReader類別中所提供的方法。
FileReader建構子
FileReader建構子 | 說明 | FileReader(File file) |
建立一個FileReader物件,並指定一個File物件以供讀取。 | FileReader(FileDescriptor fd) | 建立一個FileReader物件,並指定一個FileDescriptor以供讀取。 | FileReader(String fileName) |
建立一個FileReader物件,並指定一個File檔名以供讀取。 |
FileWriter建構子
FileReader建構子 |
說明 | FileWriter(File file) | 建立一個FileWriter並指定一的File類別物件file。 | FileWriter(FileDescriptor fd) |
建立一個FileWriter並指定一的FileDescriptor類別物件fd。 | FileWriter(String fileName) | 建立一個FileWriter並給定檔案名稱 | FileWriter(String fileName, boolean append) |
建立一個FileWriter指定一的File類別物件與boolean值append,當append=true時表示可以將新增資料加在原始資料後面,當append=false時表示新增的資料將覆蓋/取代原始資料。 |
FileWriter範例實作
本範例先利用FileWriter將字串寫到指定檔案,再利用FileReader依序讀出資料。 1. import java.io.*; 2. public class Ex_FileWriter 3. { 4. public static void main(String[] args) throws Exception
5. { 6. String s; 7. s = "Java輸入與輸出(Java.io套裝模組)"; 8. System.out.println("利用FileWriter將資料寫到檔案!"); 9. System.out.println("將字串\"" + s + "\"寫到檔案\n資料長度:" +
10. s.length() + " bytes."); 11. FileWriter fw = new FileWriter("SampleFile2.txt"); 12. fw.write(s, 0, s.length()); //直接將String寫入檔案 13. fw.close(); // 關閉檔案
14. System.out.println("\n利用FileWriter將資料從檔案裡讀出!"); 15. FileReader fr = new FileReader("SampleFile2.txt"); 16. char[] buf = new char[1]; 17. int total_chars; 18. total_chars = fr.read(buf);
19. System.out.println("讀取到的SampleFile2.txt檔案內容如下"); 20. System.out.println("----------------------------------"); 21. while (total_chars != -1) 22. { 23. System.out.print(new String(buf, 0, total_chars));
24. total_chars = fr.read(buf); 25. } 26. fr.close(); 27. } 28. } |
執行結果:
利用FileWriter將資料寫到檔案!
將字串"Java輸入與輸出(Java.io套裝模組)"寫到檔案
資料長度:22 bytes.
利用FileWriter將資料從檔案裡讀出!
讀取到的SampleFile2.txt檔案內容如下----------------------------------
Java輸入與輸出(Java.io套裝模組)

【利用FileWriter將資料寫到檔案之後SampleFile2.txt的內容】 |
程式第12行fw.write(s, 0, s.length());直接利用writer將String寫入,s.length為字串長度。
3. Processing Streams
Processing Streams是在程式中暫存或執行資料處理時的Stream物件。
Processing Streams列表
Byte Streams | Character Streams |
用途 | BufferedInputStream
BufferedOutputStream | BufferedReader
BufferedWriter | 暫存資料串流,直接從記憶體中讀取資料以增加執行上的效能。 | FilterInputStream
FilterOutputStream | FilterReader
FilterWriter | 是用來轉換資料型態的(不過不是他自己做而是由他的子類別來實作),資料流Stream可以經過filter過濾來做資料轉換的動作,值得注意的是FilterInputStream、FilterOutputStream、FilterReader與FilterWriter實際上並沒有提供任何的功能,他的實作方法都是由子類別所提供。 | | InputStreamReader
OutputStreamWriter |
可將byte的資料轉成character的資料型態,並且可以指定要用哪一種編碼方式來讀取資料。 | DataInputStream
DataOutputStream | | 利用DataInputStream可以讀取不同類型的資料。利用DataInputStream可以寫入不同類型的資料 |
ObjectInputStream
ObjectOutputStream | | 物件序列化,把物件資訊紀錄到檔案中。 | LineNumberInputStream |
LineNumberReader | 用來追蹤目前的行號,並利用getLineNumber()方法來取得行號(此類別於Java2中已經不建議被使用) | PushbackInputStream | PushbackReader | 將目前所讀到的資料(byte或character)往後退一格,以便再次重新讀取資料(當作甚麼事都沒發生過一樣) |
PrintStream | PrintWriter | 將輸入的資料顯示於螢幕上,其資料輸入來源是Stream。 |
FilterInputStream是InputStream的子類別。FilterInputStream建構子的存取權限被設定為protected,所以只有它的父類別InputStream才可以使用,無法在一般程式中呼叫使用。在實作上,只有java.io函式庫中的類別才可以呼叫FilterInputStream建構子,因此在處理上必須將資料包裝在InputStream的封包中。
‧FilterInputStream與FilterOutputStream
FilterInputStream建構子
FileReader建構子 |
說明 | FilterOutputStream(OutputStream out) | 藉由所傳入的OutputStream物件建立一個FilterOutputStream。注意:所傳入的out是指OutputStream下所有的子類別皆可傳入並建立FilterOutputStream物件。 |
‧FilterReader與FilterWriter
FilterReader建構子
FileReader建構子 | 說明 |
FilterReader(Reader in) | 藉由所傳入的Reader物件建立一個FilterReader。注意:FilterReader建構子的存取權限設定是protected。 |
FilterWriter建構子
FileReader建構子 | 說明 | FilterWriter (Writer out) | 藉由所傳入的Writer物件建立一個Filter Writer。注意:Filter Writer建構子的存取權限設定也是protected。 |
‧DataInput與DataOutput介面
實作DataInput或DataOutput介面的類別將實作出這二個介面提供的所有方法,使其可以讀取或寫入各種型態的資料(資料類型為java primitive types與UTF-8 formate),而DataInputStream、DataOutputStream與RandomAccessFile也實作了此介面並提供了介面中所有的方法。
DataInput介面所提供的方法
DataInput類別介面提供了15種方法,除了skipBytes()方法外,DataInput提供的方法名稱全是以 “read”為字首。
方法名稱 | 傳回值 | 說明 |
readBoolean() | boolean | 若讀取的byte(位元組)若為0則回傳值為false,反之則回傳true。 | readByte() | byte |
讀取一個byte。 | readChar() | char | 讀取一個character(字元)。 |
readDouble() | double | 讀取double數值(8-bytes)。 | readFloat() | float |
讀取float數值(4-bytes)。 | readInt() | int | 讀取int數值(4-bytes)。 |
readLong() | long | 讀取long數值(8-bytes)。 | readShort() | short |
讀取short數值(2-bytes)。 | readUnsignedByte() | int | 讀取1個Unsigned位元組資料,並傳回int型態的資料(0~255)。 |
readUnsignedShort() | int | 讀取Unsigned short資料,並傳回int型態的資料(0~65536)。 | readUTF() | String |
讀取UFT-8格式的資料字串。 | readFully(byte[] b) | void | 將從Stream中讀取到的資料放入b中(b為緩衝區位元陣列) |
readFully(byte[] b, int off, int, len) | void | 於Stream中讀取到的資料中從off位置開始讀取len個位元組資料並填入到b中。 | readLine() | String |
讀取Stream中下一行的文字 | skipBytes(int n) | int | 讀取第n個位置之後的位元組資料。 |
DataInputStream與RandomAccessFile類別必須實作DataInput類別介面中所有的方法。
‧DataOutput介面所提供的方法
DataOutput類別介面提供了14種方法,方法名稱全是以 “write”為字首且傳回值都是void(也就是沒有傳回值)。 方法名稱 | 傳回值 |
說明 | write(byte[] b) | void | 將緩衝區的位元組陣列資料b寫到輸出資料流output stream。 |
write(byte[] b, int off, int len) | void | 將緩衝區的位元組陣列資料b中從off位置開始寫入len個位元組資料到output stream。 | write(int b) | void |
將int位元資料(8個低階位元- eight low-order)寫到output stream。請注意寫入的不是b的數值資料而是b的位元資料。 | writeBoolean(Boolean b) | void | 將b的布林值寫入output stream。 |
writeByte(int v) | void | 將int位元資料(8個低階位元- eight low-order)寫到output stream。 | writeBytes(String s) | void |
將s字串資料寫入output stream。 | writeChar(int v) | void | 將int位元組資料寫到output stream。也就是寫入字元的資料型態。 |
writeChars(String s) | void | 依序將s字串中的每一個字元資料寫到output stream(每1個char是2個bytes) | writeDouble(double v) | void |
將double數值資料寫入output stream(8-bytes)。 | writeFloat(float f) | void | 將float數值資料寫入output stream(4-bytes)。 |
writeInt(int v) | void | 將int數值資料寫入output stream(4-bytes)。 | writeLong(long v) | void |
將long數值資料寫入output stream(8-bytes)。 | writeShort(int v) | void | 將short數值資料寫入output stream(2-bytes)。 |
writeUTF(String str) | void | 將str字串資料中的每一個字元用2個bytes寫到output stream,而每一個字元的編碼方式是:Java-modifirf UTF |
DataOutputStream與RandomAccessFile類別必須實作DataOutput類別介面中所有的方法。
‧Java I/O 資料串流鏈結
實作上撰寫Java I/O程式時,為求效能與彈性,通常不會只用單一I/O物件來存取Source與Sink端中的資料(例如只使用FileReader與FileWriter),而會搭配不同I/O物件個別特性與彼此間的關係來達到目的,這種使用多種I/O物件之間彼此資料交換的行為我們稱之為 “資料串流鏈結”,同樣的也分為2種鏈結模式分別是InputStream鏈結模式與OutputStream鏈結模式。
InputStream鏈結模式:將資料自來源端接收後傳入記憶體中;
DataSource(Source) a Memory/Program(Sink)
OutputStream鏈結模式:將資料自來源端接收後傳入記憶體中;
Memory/Program(Source) a DataSink(Sink)
有關NodeStream與ProcessStream需求量的多寡會依照實際開發的需求而有所不同。
‧BufferedReader與BufferedWriter
利用資料緩衝區(Buffer)可大量降低檔案操作上的次數以增加程式執行上的效率。BufferedReader與Bufferedwriter是字元導向(char-oriented),讀取資料時不需逐字讀取,可利用readLine()作逐行讀取以提升資料讀取的效率。另外還有一組BufferedInputStream與BufferedOutputStream是位元導向(byte-oriented),資料讀取大致上與BufferedReader與Bufferedwriter相同,通常在input是一個stream且output也是一個stream時才比較容易使用到。以下針對BufferedReader與Bufferedwriter作詳盡說明。 BufferedReader建構子 FileReader建構子 | 說明 |
BufferedReader(Reader in) | 建立一個BufferReader物件,其參數in必須屬於Reader物件 | BufferedReader(Reader in, int sz) | 建立一個BufferReader物件,其參數in必須屬於Reader物件。sz為設定buffer大小的參數,一般來說sz都會大於0,這樣的設定才有意義;若sz<=0系統便會丟出IllegalArgumentException例外 |
BufferedReader常用方法 方法名稱 |
傳回值 | 說明 | close() | void |
關閉BufferedrReader物件的方法 | mark(int readAheadLimit) | void | 標示目前所讀取到的位置,readAheadLimit表示標示後到reset前的長度 |
markSupported() | boolean | 判斷所輸入的Stream是否支援mark與reset:支援傳回true,反之則傳回false. | read() |
int | 讀取一個字元(character)資料 | read(char[] cbuf, int off, int len) | int | 將所指定位置與長度的資料讀入到cbuf緩衝器中。off:指定資料位置(並由此開始取資料),len:資料長度 |
readLine() | String | 讀取一行文字資料(a line of text) | ready() |
boolean | 判斷BufferedReader已經準備好可以讀取資料了,可以傳回true,反之則傳回false | reset() | void | 讀取到最近一次利用mark()方法所設定的位置 |
skip(long n) | long | 從目前讀取的資料位置跳過n個字元,並將跳過後的資料位置為新的讀取位置 |
|
BufferedWriter建構子 BufferedWriter建構子 | 說明 |
BufferedWriter(Writer out) | 建立一個BufferWriter物件,其參數out須屬於Writer物件 | BufferedReader(Reader in, int sz)BufferedWriter(Writer out, int sz) | 建立一個BufferWriter物件,其參數out必須屬於Reader物件,sz為設定buffer大小的參數 |
‧BufferedWriter常用方法 方法名稱 |
傳回值 | 說明 | close() | void | 關閉BufferedWriter物件的方法 |
flush() | void | 強制將Buffer(緩衝器)內的資料寫入BufferedWriter物件 | newLine() |
void | 加入新的一行(Write a line separator) | write(char[] cbuf, int off, int len) | void | 將所指定位置與長度的資料寫到cbuf緩衝器中。off:指定資料位置(並由此開始取資料),len:資料長度 |
write(int c) | void | 將資料寫到BufferedWriter物件中 | write(String s, int off, int len) |
void | 將所指定位置與長度的資料寫到BufferedWriter物件中 |
|
BufferedReader與BufferedWriter範例程式
BufferedReader與BufferedWriter範例程式 |
1. import java.io.*;
2. public class Ex_BufferedRW 3. { 4. public static void main(String[] args) throws Exception 5. { 6. // 利用BufferedReader讀取SampleFile.txt的原始資料 7. FileReader fr1 = new FileReader("SampleFile.txt");
8. BufferedReader buf_fr1 = new BufferedReader(fr1); 9. String strReadLine; 10. System.out.println("利用BufferedReader讀取SampleFile.txt的原始資料如下"); 11. System.out.println("----------------------------------"); 12. strReadLine = buf_fr1.readLine(); // 一次讀取一行
13. while (strReadLine != null) // 判斷是否有抓到資料 14. { 15. System.out.println(strReadLine); 16. strReadLine = buf_fr1.readLine(); 17. } 18. buf_fr1.close(); //只要關閉Buffer物件即可,因為其他與之相關聯的資源也會一起被關閉
19. // 利用BufferedWriter將指定字串寫到SampleFile.txt 20. int i = 0; 21. String[] strWriteLine = new String[2]; 22. strWriteLine[0] = "Howard Tuan"; 23. strWriteLine[1] = "Baby Tuan";
24. FileWriter fw = new FileWriter("SampleFile.txt", true); 25. BufferedWriter buf_fw = new BufferedWriter(fw); 26. for(i=0;i<strWriteLine.length;i++) 27. { 28. buf_fw.write(strWriteLine[i], 0, strWriteLine[i].length());
29. buf_fw.newLine(); 30. } 31. buf_fw.close(); 32. // 再利用BufferedReader讀取修改過後SampleFile.txt的資料 33. FileReader fr2 = new FileReader("SampleFile.txt");
34. BufferedReader buf_fr2 = new BufferedReader(fr2); 35. System.out.println("\n再利用BufferedReader讀取修改過後的資料如下 "); 36. System.out.println("----------------------------------"); 37. strReadLine = buf_fr2.readLine(); // 一次讀取一行 38. while (strReadLine != null) // 判斷是否有抓到資料
39. { 40. System.out.println(strReadLine); 41. strReadLine = buf_fr2.readLine(); 42. } 43. buf_fr2.close(); 44. }
45. } | 執行結果:
利用BufferedReader讀取SampleFile.txt的原始資料如下
----------------------------------

|
4. RandomAccessFile RandomAccessFile是唯一可以同時讀取(r)與寫入(rw)資料的類別,並且可利用指標(這裡所說的指標是檔案指標file pointer)來指定所要讀取或寫入的位置,若開啟的檔案不存在,則系統會自動建立一個新檔,其檔案指標會指向0的位置(預設為0)。
‧RandomAccessFile建構子
BufferedWriter建構子 |
說明 | RandomAccessFile(File file, String mode) | 根據所指定的file物件來建立一個RandomAccessFile物件,並設定其存取模式,若mode = r表示可讀不可寫(唯讀),mode = rw則表示可讀可寫。 | RandomAccessFile(String name, String mode) |
根據所指定的檔案名稱name來建立一個RandomAccessFile物件,並設定其存取模式。 |
RandomAccessFile常用方法
RandomAccessFile實作了DataInput與DataOutput,擁有檔案的資料讀取與寫入功能,因此必須實作這二個介面類別中所有的方法。以下便介紹RandomAccessFile的檔案指標存取方法。
方法名稱 | 傳回值 | 說明 |
getFilePointer() | long | 讀取目前的檔案指標(也就是目前在檔案中的位置) | seek(long pos) | void |
將檔案指標指向指定位置(pos),後續的讀取或寫入的動作將從這個位置開始。 |
|
RandomAccessFile範例程式
範例Part I簡單利用了RandomAccessFile類別中所提供的write()方法將資料寫到檔案中,再以readLine()方法逐行讀出資料。請注意程式中二個RandomAccessFile()類別建構子的參數設定。
RandomAccessFile範例程式 – Part I |
1.
import java.io.*; 2. public class Ex_RandomAccessFile
3. {
4. public static void main(String[] args) throws Exception
5.
{
6. String s;
7. s = "Java SCJP\nJDK 1.3.1";
8. byte[] newData = s.getBytes();
9.
RandomAccessFile rf1 = new RandomAccessFile("SampleFile.txt", "rw");
10.
System.out.println("利用RandomAccessFile將資料寫到檔案!");
11.
System.out.println("將字串\"" + s + "\"寫到檔案\n資料長度:" +
12. s.length() + " bytes.");
13. rf1.write(newData);
14.
rf1.close();
15. RandomAccessFile rf2 = new RandomAccessFile("SampleFile.txt", "r");
16. System.out.println("\n利用RandomAccessFile讀取到的" +
17. "SampleFile.txt檔案內容如下");
18. System.out.println("----------------------------------");
19. while ((s = rf2.readLine()) != null )
20.
System.out.println(s);
21. rf2.close();
22. }
23. }
|
執行結果:
利用RandomAccessFile將資料寫到檔案! 將字串"Java SCJP JDK 1.3.1"寫到檔案 資料長度:19 bytes.
利用RandomAccessFile讀取到的SampleFile.txt檔案內容如下 ----------------------------------
Java SCJP JDK 1.3.1
|
程式第9行宣告RandomAccessFile類別的存取模式是rw(可讀寫),第15行只用到r(意即唯讀),因後續動作只是讀取與顯示資料,不需寫入資料,所以只要有唯讀的權限就夠了。
範例Part II則利用檔案指標(file pointer)所指定的位置來作讀取和寫入資料。
RandomAccessFile範例程式 – Part II |
1. import java.io.*;
2. public class Ex_RandomAccessFile2
3.
{
4. public static void main(String[] args) throws Exception
5. {
6. String s;
7.
RandomAccessFile rf = new RandomAccessFile("Buffer.txt", "rw");
8.
// RandomAccessFile物件建立之初時的指標位置
9.
System.out.println("利用getFilePointer()方法傳回目前的檔案指標位置:" +
rf.getFilePointer());
10. // -- Insert Data
11.
rf.writeUTF("My name is ");
12.
long fpointer = rf.getFilePointer();
13. System.out.println("加入UTF字串(\"My name is \")後的檔案指標位置:" + fpointer);
14. rf.writeUTF("Vincent");
15. System.out.println("加入UTF字串(\"Vincent\")後的檔案指標位置:" +
rf.getFilePointer());
16. rf.seek(0);
17. System.out.println("將所加入的2組UTF字串資料秀出:" + rf.readUTF() +
rf.readUTF());
18. // -- Modify Data
19. rf.seek(fpointer);
20. System.out.println("\n將指標指向" + fpointer + "並加入UTF字串(\"Anita\")");
21. rf.writeUTF("Anita");
22. rf.seek(0);
23.
System.out.println("最後將變更後的UTF字串資料秀出:" + rf.readUTF() +
rf.readUTF());
24. rf.close();
25.
}
26. }
| 執行結果: 利用getFilePointer()方法傳回目前的檔案指標位置:0 加入UTF字串("My name is ")後的檔案指標位置:13
加入UTF字串("Vincent")後的檔案指標位置:22 將所加入的2組UTF字串資料秀出:My name is Vincent
將指標指向13並加入UTF字串("Anita") 最後將變更後的UTF字串資料秀出:My name is Anita
|
程式第7行所宣告RandomAccessFile類別的存取模式是rw(可讀寫),第9行利用getFilePointer()方法取得目前在檔案中的位置。
由於RandomAccessFile物件是初始建立,因此傳回檔案指標值為0(file pointer=0)。第11行利用writeUFT()方法將”My name is ”以Unicode編碼方式寫入檔案。第12行再利用getFilePointer()方法取得目前在檔案中的位置,此時的檔案指標是在寫入字串之後(亦即程式第11行執行完畢後)的位置(file pointer=13),所取得的檔案指標位置將指派給fpointer變數。第14行利用writeUFT()方法將”Vincent ”以Unicode編碼方式寫入檔案。第15行即利用getFilePointer()方法取得目前在檔案中的位置,並顯示在螢幕上。
第17行將所輸入的Unicode字串顯示在螢幕上,由於檔案指標位置已指向檔尾,必須先將指標移向檔頭(seek(0))後才能利用readUFT()方法將Unicode字串印出,否則會出現Exception(因指標指向檔尾,指標後已無任何資料可供readUTF()方法讀取)。
請注意,第17行呼叫了兩次readUFT()方法,是因為在程式第11與第14行各使用了writeUFT()方法一次,一個writeUTF()會依序對應一個readUTF(),因此第17行才呼叫兩次readUFT()方法。
第19行我們再用seek()方法將檔案指標內容值指向先前的fpointer變數(file pointer=13)。第25行利用writeUFT()方法將 “Anita ”以Unicode編碼方式寫入檔案,從檔案指標13開始,在檔案指標之後的資料將會被新加入的Unicode字串所覆蓋。第23行在螢幕上顯示變更後的Unicode字串。
圖解RandomAccessFile程式範例Part II:
程式第11行:

程式第19~23行:
將Unicode字串 “Anita”從檔案指標第13的位置插入,並覆蓋指標後的字串資料。

程式第23行:
變更後的UTF字串資料

5. 資料編碼轉換- InputStreamReader與OutputStreamWriter
作業系統會根據不同的編碼方式來儲存字元,有些編碼方式以1個位元組(1 bytes)表示字元(1 char)資料,有些則以2個位元組(2 bytes)來表示。Java本身字元集的編碼方式為Unicode ,並且提供了不同類型的字元集以因應各種作業系統的編碼方式。
資料編碼轉換常應用於透過網路存取資料,資料的來源端與目的端可能是不同的機器,資料的編碼方式也不一樣。例如:在Window系統中所使用的字元集是ISO 8859-1(ANSI),而在Macintosh中所使用的字元集則是Macintosh Latin-2,因此輸入資料時就必須利用InputStreamReader作資料編碼的轉換(decode),以便後續程式運作。
InputStreamReader是將byte streams的資料轉換為character streams的橋樑,可利用指定的字元集將byte解碼(decode)至character中讀取。InputStreamReader建構子為InputStreamReader(InputStream in, String charsetName),其中in是資料來源端InputStream物件,charsetName是字元集名稱(charset name)。
OutputStreamWriter是將character streams的資料轉換為byte streams的橋樑,可利用指定的字元集並利用write()方法將character編碼(encoded)到bytes中。OutputStreamWrite建構子為OutputStreamWriter(OutputStream out, String charsetName),其中out是資料輸出端OutputStream物件,charsetName為字元集名稱(charset name)。
OutputStreamWrite程式範例
OutputStreamWrite資料輸出編碼(encoded)程式範例 |
1. … // Block of code 2. … 3. FileOutputStream fo = new FileOutputStream (“sample.txt”); 4. OutputStreamWriter OutWriter = new OutputStreamWriter (fo, “ISO2022CN”); 5. …
6. … // Block of code |
程式第4行利用 “ISO2022CN”為output的資料進行編碼( “ISO2022CN”是描述中文的字元集)。
getEncoding()方法
方法名稱 | 傳回值 | 說明 |
getFilePointer() | long | 讀取目前的檔案指標(也就是目前在檔案中的位置) | seek(long pos) | void |
將檔案指標指向指定位置(pos),後續的讀取或寫入的動作將從這個位置開始。 |
|
|
沒有留言:
張貼留言