1、File
不同的操作系統對于檔案系統路徑的設定各有差別,例如在Windows中,一個路徑的表示法可能是:
"c:\\Windows\\Fonts\\"
而在Linux下的路徑設定可能是:
"/home/justin/"
Windows的路徑指定是使用UNC(Universal Naming Convention)路徑名,以\\開始表示磁盤根目錄,如果沒有以\\開始表示相對路徑,c是可選的磁盤指定,后面跟隨著 : 字符。而UNIX-Like系統的路徑指定以 / 開始表示絕對路徑,不以 / 開始表示相對路徑。
因而在程序中設定路徑時會有系統相依性的問題,File類別提供一個抽象的、與系統獨立的路徑表示,您給它一個路徑字符串,它會將它轉換為與系統無關的抽象路徑表示,這個路徑可以指向一個檔案、目錄或是URI,您可以用以下四種方式來建構File的實例:
File(File parent, String child) File(String pathname) File(String parent, String child) File(URI uri)
一個File的實例被建立時,它就不能再被改變內容;File類別除了用來表示一個檔案或目錄的抽象表示之外,它還提供了不少相關操作方法,您可以用它來對檔案系統作一些查詢與設定的動作。
來看個簡單的程序:
FileDemo.java
package onlyfun.caterpillar; import java.io.*; import java.util.*; public class FileDemo {public static void main(String[] args) {try {File file = new File(args[0]);if(file.isFile()) { // 是否為檔案System.out.println(args[0] + " 檔案");System.out.print(file.canRead() ? "可讀 " : "不可讀 ");System.out.print(file.canWrite() ? "可寫 " : "不可寫 ");System.out.println(file.length() + "字節");}else {// 列出所有的檔案及目錄File[] files = file.listFiles();ArrayList fileList =new ArrayList();for(int i = 0; i < files.length; i++) {// 先列出目錄if(files[i].isDirectory()) { //是否為目錄// 取得路徑名System.out.println("[" +files[i].getPath() + "]");}else {// 檔案先存入fileList,待會再列出fileList.add(files[i]);}}// 列出檔案for(File f: fileList) {System.out.println(f.toString());}System.out.println();}}catch(ArrayIndexOutOfBoundsException e) {System.out.println("using: java FileDemo pathname");}} }
執行結果:
java onlyfun.caterpillar.FileDemo C:\ [C:\WINDOWS] [C:\Documents and Settings] [C:\Program Files] [C:\System Volume Information] [C:\Recycled] C:\A3N_A3L.10 C:\bootfont.bin C:\ntldr C:\NTDETECT.COM C:\boot.ini C:\CONFIG.SYS C:\AUTOEXEC.BAT C:\IO.SYS C:\MSDOS.SYS C:\Finish.log C:\pagefile.sys C:\VIRTPART.DAT
9、RandomAccessFile
檔案存取通常是「循序的」,每在檔案中存取一次,讀取檔案的位置就會相對于目前的位置前進,然而有時候您必須對檔案的某個區段進行讀取或寫入的動作,也就是進行「隨機存取」(Random access),也就是說存取檔案的位置要能在檔案中隨意的移動,這時您可以使用RandomAccessFile,使用seek()方法來指定檔案存取的位置,指定的單位是字節,藉由它您就可以對檔案進行隨機存取的動作。
為了方便,通常在隨機存取檔案時會固定每組資料的長度,例如一組學生個人數據,Java中并沒有像C/C++中可以直接寫入一個固定長度結構(Structure)的方法,所以在固定每組長度的方面您必須自行設計。
下面這個程序示范了如何使用RandomAccessFile來寫入檔案,并隨機讀出一筆您所想讀出的資料:
Student.java
package onlyfun.caterpillar; public class Student {private String name; // 固定 15 字符private int score;public Student() {setName("noname");}public Student(String name, int score) {setName(name);this.score = score;}public void setName(String name) {StringBuilder builder = null;if(name != null)builder = new StringBuilder(name);elsebuilder = new StringBuilder(15);builder.setLength(15);this.name = builder.toString();}public void setScore(int score) {this.score = score;}public String getName() {return name;}public int getScore() {return score;}// 每筆數據固定寫入34字節public static int size() {return 34;} }
RandomAccessFileDemo.java
package onlyfun.caterpillar; import java.io.*; import java.util.*; public class RandomAccessFileDemo {public static void main(String[] args) {Student[] students = {new Student("Justin", 90),new Student("momor", 95),new Student("Bush", 88),new Student("caterpillar", 84)};try {File file = new File(args[0]);// 建立RandomAccessFile實例并以讀寫模式開啟檔案RandomAccessFile randomAccessFile =new RandomAccessFile(file, "rw");for(int i = 0; i < students.length; i++) {randomAccessFile.writeChars(students[i].getName());randomAccessFile.writeInt(students[i].getScore());}Scanner scanner = new Scanner(System.in);System.out.print("讀取第幾筆數據?");int num = scanner.nextInt();randomAccessFile.seek((num-1) * Student.size());Student student = new Student();student.setName(readName(randomAccessFile));student.setScore(randomAccessFile.readInt());System.out.println("姓名:" + student.getName());System.out.println("分數:" + student.getScore());randomAccessFile.close();}catch(ArrayIndexOutOfBoundsException e) {System.out.println("請指定文件名稱");}catch(IOException e) {e.printStackTrace();}}private static String readName(RandomAccessFile randomAccessfile)throws IOException {char[] name = new char[15];for(int i = 0; i < name.length; i++)name[i] = randomAccessfile.readChar();return new String(name).replace('\0', ' ');} }
在實例化一個RandomAccessFile對象時,要設定檔案開啟的方式,設定"r"表示只供讀取,設定"rw"表示可讀可寫;為了讓每組數據長度固 定,在寫入name時,我們使用 StringBuilder 并設定其長度固定為15個字符,而讀回name時則直接讀回15個字符,然后再去掉空格符傳回。
10、InputStream與OutputStream
計算機中的數據都是以0與1的方式來儲存,如果您要在兩個裝置之間進行數據的存取,當然也是以0與1位的方式來進行,實際上數據的流動是透過電路,而上面 的數據則是電流,而在程序上來說,將數據目的地與來源之間抽象化為一個串流(Stream),而當中流動的則是位數據。
01010101 Stream -->
來源地 ===================== 目的地
在Java中有兩個類別用來作串流的抽象表示:InputStream與OutputStream。
InputStream是所有表示位輸入串流的類別之父類別,它是一個抽象類別,子類會重新定義它當中所定義的方法, InputStream用于從裝置來源地讀取數據的抽象表示,例如System中的標準輸入串流 in 對象就是一個 InputStream,在程序開始之后,這個串流對象就會開啟,以從標準輸入裝置中讀取數據,這個裝置通常是鍵盤或是其它使用者定義的裝置。
OutputStream是所有表示位輸出串流的類別之父類別,它是一個抽象類別,子類會重新定義它當中所定義的方法, OutputStream是用于將數據寫入目的地的抽象表示,例如System中的標準輸出串流對象 out ,out 的類型是PrintStream, 這個類別是OutputStream的子類別(FilterOutputStream繼承OutputStream, PrintStream再繼承FilterOutputStream),在程序開始之后,這個串流對象就會開啟,您可以將數據透過它來寫入目的地裝置,這 個裝置通常是屏幕或其它使用者定義的裝置。
下面程序可以讀取鍵盤輸入串流,并將資料以10進位方式顯示在屏幕上:
StreamDemo.java
package onlyfun.caterpillar; import java.io.*; public class StreamDemo {public static void main(String[] args) {try {System.out.print("輸入字符: ");System.out.println("輸入字符十進制表示: " +System.in.read());System.out.println("換行字符十進制表示: " +System.in.read());}catch(IOException e) {e.printStackTrace();}} }
執行結果:?
輸入字符: A? 輸入字符十進制表示: 65 換行字符十進制表示: 10
字符A輸入后被標準輸入串流讀取,A的位表示以十進制來看就是65,這是A字符的編碼(查查ASCII編碼表就知道了),在這邊要注意的是read()只讀取一個字節的數據,而當輸入A并按Enter鍵時,實際上在串流中會有A的位數據與換行字符的位數據,換行字符的位數據以十進制來表示的話就是10。
操作系統之間的換行字符各不相同,Windows 為"\r\n",Linux 為'\n',而 Mac 為'\r'。
11、BufferedInputStream、 BufferedOutputStream
在介紹 FileInputStream、 FileOutputStream的 例子中,您使用了一個數組來作為數據讀入的緩沖區,以檔案存取為例的話,您知道磁盤存取的速度是遠低于內存中的數據存取速度,為了減少對磁盤的存 ,您一次讀入一定長度的數據,如上一個主題范例中的1024字節,而寫入時也是一次寫入一定長度的數據,這可以增加數據存取的效率。
BufferedInputStream與BufferedOutputStream可以為InputStream類的對象增加緩沖區功能,使用它們,您無需自行設計緩沖區。
BufferedInputStream的數據成員buf是個位數組,預設為2048字節大小,當讀取數據來源時,例如檔案, BufferedInputStream會盡量將buf填滿,當使用read()方法時,實際上是先讀取buf中的數據,而不是直接對數據來源作讀取,當buf中的數據不足時,BufferedInputStream才會再從數據來源中提取數據。
BufferedOutputStream的數據成員buf是個位數組,預設為512個字節,當寫入數據時,會先將資料存至buf中,當buf已滿時才會一次將數據寫至目的地,而不是每次寫入都對目的地作寫入。
將上一個主題的范例作個改寫,這次不用自行設定緩沖區并進行判斷了,使用BufferedInputStream、 BufferedOutputStream讓程序看來簡單一些,也比較有效率:
BufferedStreamDemo.java
package onlyfun.caterpillar; import java.io.*; public class BufferedStreamDemo {public static void main(String[] args) {try {byte[] data = new byte[1];File srcFile = new File(args[0]);File desFile = new File(args[1]);BufferedInputStream bufferedInputStream =new BufferedInputStream(new FileInputStream(srcFile));BufferedOutputStream bufferedOutputStream =new BufferedOutputStream(new FileOutputStream(desFile));System.out.println("復制檔案:" +srcFile.length() + "字節");while(bufferedInputStream.read(data) != -1) {bufferedOutputStream.write(data);}// 將緩沖區中的數據全部寫出bufferedOutputStream.flush();// 關閉串流bufferedInputStream.close();bufferedOutputStream.close();}catch(ArrayIndexOutOfBoundsException e) {System.out.println("using: java UseFileStream src des");e.printStackTrace();}catch(IOException e) {e.printStackTrace();}} }
為了確保緩沖區中的數據一定被寫出,建議最后執行flush()將緩沖區中的數據全部寫出目的串流中。
BufferedInputStream、BufferedOutputStream并沒有改變來源InputStream或目的 OutputStream的行為,讀入或寫出時的動作還是InputStream、OutputStream負責, BufferedInputStream、BufferedOutputStream只是在這之前動態的為它們加上一些功能(像是緩沖區功能),在這邊是 以檔案存取串流為例,實際上您可以在其它串流對象上加上BufferedInputStream、BufferedOutputStream功能。
12、FileInputStream、 FileOutputStream
FileInputStream是InputStream的子類,由名稱上就可以知道, FileInputStream主要就是從指定的檔案中讀取數據至目的地。
FileOutputStream是OutputStream的子類,顧名思義,FileInputStream主要就是從來源地寫入數據至指定的檔案中。
標準輸入輸出串流對象在程序一開始就會開啟,但只有當您建立一個FileInputStream或FileOutputStream的實例時,實際的串流才會開啟,而不使用串流時,也必須自行關閉串流,以釋放與串流相依的系統資源。
下面這個程序可以復制檔案,程序先從來源檔案讀取數據至一個位緩沖區中,然后再將位數組的數據寫入目的檔案:
FileStreamDemo.java
package onlyfun.caterpillar; import java.io.*; public class FileStreamDemo {public static void main(String[] args) {try {byte[] buffer = new byte[1024];FileInputStream fileInputStream =new FileInputStream(new File(args[0]));FileOutputStream fileOutputStream =new FileOutputStream(new File(args[1]));System.out.println("復制檔案:" +fileInputStream.available() + "字節");while(true) { // 從來源檔案讀取數據至緩沖區if(fileInputStream.available() < 1024) {int remain;while((remain = fileInputStream.read())!= -1) {fileOutputStream.write(remain);}break;}else {fileInputStream.read(buffer);// 將數組數據寫入目的檔案fileOutputStream.write(buffer);}}// 關閉串流fileInputStream.close();fileOutputStream.close();System.out.println("復制完成");}catch(ArrayIndexOutOfBoundsException e) {System.out.println("using: java FileStreamDemo src des");e.printStackTrace();}catch(IOException e) {e.printStackTrace();}} }
這個程序示范了兩個 read() 方法,一個可以讀入指定長度的數據至數組,一個一次可以讀入一個字節,每次讀取之后,讀取的指標都會往前進,您使用available()方法獲得還有多少字節可以讀取;除了使用File來建立FileInputStream、FileOutputStream的實例之外,您也可以直接使用字符串指定路徑來建立。
不使用串流時,記得使用close()方法自行關閉串流,以釋放與串流相依的系統資源。
13、ObjectInputStream、ObjectOutputStream
在Java這樣支持對象導向的程序中撰寫程序,很多數據都是以對象的方式存在,在程序運行過后,您會希望將這些數據加以儲存,以供下次執行程序時使用,這時您可以使用ObjectInputStream、ObjectOutputStream來進行這項工作。
要被儲存的對象必須實作Serializable接口,說是實作,其實Serializable中并沒有規范任何必須實作的方法,所以這邊所謂實作的意義,其實像是對對象貼上一個標志,代表該對象是可以序列化的(Serializable)。
一個實作的例子如下所示:
Student.java
package onlyfun.caterpillar; import java.io.*; public class Student implements Serializable {private static final long serialVersionUID = 1L;private String name;private int score;public Student() {name = "N/A";}public Student(String name, int score) {this.name = name;this.score = score;}public void setName(String name) {this.name = name;}public void setScore(int score) {this.score = score;}public String getName() {return name;}public int getScore() {return score;}public void showData() {System.out.println("name: " + name);System.out.println("score: " + score);} }
您要注意到serialVersionUID,這代表了可序列化對象的版本, 如果您沒有提供這個版本訊息,則會自動依類名稱、實現的接口、成員等訊息來產生,如果是自動產生的,則下次您更改了Student類,則自動產生的 serialVersionUID也會跟著變更,當反序列化時兩個serialVersionUID不相同的話,就會丟出 InvalidClassException,如果您想要維持版本訊息的一致,則要顯式宣告serialVersionUID。
ObjectInputStream、ObjectOutputStream為InputStream、OutputStream加上了可以讓使用者寫入 對象、讀出對象的功能,在寫入對象時,我們使用writeObject()方法,讀出對象時我們使用readObject()方法,被讀出的對象都是以 Object的型態傳回,您必須將之轉換為對象原來的型態,才能正確的操作被讀回的對象,下面這個程序示范了如何簡單的儲存對象至檔案中,并將之再度讀 回:
ObjectStreamDemo.java
package onlyfun.caterpillar; import java.io.*; import java.util.*; public class ObjectStreamDemo {public static void writeObjectsToFile(Object[] objs, String filename) {File file = new File(filename);try {ObjectOutputStream objOutputStream =new ObjectOutputStream(new FileOutputStream(file));for(Object obj : objs) {objOutputStream.writeObject(obj);}objOutputStream.close();}catch(IOException e) {e.printStackTrace();}}public static Object[] readObjectsFromFile(String filename)throws FileNotFoundException {File file = new File(filename);if(!file.exists())throw new FileNotFoundException();List list = new ArrayList();try {FileInputStream fileInputStream =new FileInputStream(file);ObjectInputStream objInputStream =new ObjectInputStream(fileInputStream);while(fileInputStream.available() > 0) {list.add(objInputStream.readObject());}objInputStream.close();}catch(ClassNotFoundException e) {e.printStackTrace();}catch(IOException e) {e.printStackTrace();}return list.toArray();}public static void appendObjectsToFile(Object[] objs, String filename)throws FileNotFoundException {File file = new File(filename);if(!file.exists())throw new FileNotFoundException();try {ObjectOutputStream objOutputStream =new ObjectOutputStream(new FileOutputStream(file, true)) {protected void writeStreamHeader()throws IOException {}};?for(Object obj : objs) {objOutputStream.writeObject(obj);}objOutputStream.close();}catch(IOException e) {e.printStackTrace();}}public static void main(String[] args) {Student[] students = {new Student("caterpillar", 90),new Student("justin", 85)};// 寫入新檔writeObjectsToFile(students, "data.dat");try {// 讀取檔案數據Object[] objs = readObjectsFromFile("data.dat");for(Object obj : objs) {((Student) obj).showData();}System.out.println();students = new Student[2];students[0] = new Student("momor", 100);students[1] = new Student("becky", 100);// 附加至檔案appendObjectsToFile(students, "data.dat");// 讀取檔案數據objs = readObjectsFromFile("data.dat");for(Object obj : objs) {((Student) obj).showData();}}catch(FileNotFoundException e) {e.printStackTrace();}} }
對象被寫出時,會寫入對象的類別型態、類別署名(Class signature),static與被標志為transient的成員則不會被寫入。
在這邊注意到以附加的形式寫入數據至檔案時,在試圖將對象附加至一個先前已寫入對象的檔案時,由于ObjectOutputStream在 寫入數據時,還會加上一個特別的標示頭,而讀取檔案時會檢查這個標示頭,如果一個檔案中被多次附加對象,那么該檔案中會有多個標示頭,如此讀取檢查時就會 發現不一致,這會丟出StreamCorrupedException,為此,您重新定義ObjectOutputStream的writeStreamHeader()方法,如果是以附加的方式來寫入對象,就不寫入標示頭:
ObjectOutputStream objOutputStream =
new ObjectOutputStream(
new FileOutputStream(file, true)) {
protected void writeStreamHeader()
throws IOException {}
};
將對象寫出或讀入并不僅限于檔案存取,您也可以用于網絡的數據傳送,例如傳送整個對象數據或是影像檔案。
14、DataInputStream、DataOutputStream
DataInputStream、DataOutputStream可提供一些對Java基本數據型態寫入的方法,像是讀寫int、double、 boolean等的方法,由于Java的數據型態大小是規定好的,在寫入或讀出這些基本數據型態時,就不用擔心不同平臺間資料大小不同的問題。
這邊還是舉檔案存取來進行說明,有時候您只是要儲存一個對象的成員數據,而不是整個對象的信息,成員數據的型態假設都是Java的基本數據型態,您不必要 使用Object輸入、輸出相關串流對象,而可以使用DataInputStream、DataOutputStream來寫入或讀出數據,下面這個程序 是個簡單的示范:
Student.java
package onlyfun.caterpillar; public class Student?{private String name;private int score;public Student() {name = "N/A";}public Student(String name, int score) {this.name = name;this.score = score;}public void setName(String name) {this.name = name;}public void setScore(int score) {this.score = score;}public String getName() {return name;}public int getScore() {return score;}public void showData() {System.out.println("name: " + name);System.out.println("score: " + score);} }
DataStreamDemo.java
package onlyfun.caterpillar; import java.io.*; public class DataStreamDemo {public static void main(String[] args) {Student[] students = {new Student("Justin", 90),new Student("momor", 95),new Student("Bush", 88)};try {DataOutputStream dataOutputStream =new DataOutputStream(new FileOutputStream("data.dat"));for(Student student : students) {dataOutputStream.writeUTF(student.getName());dataOutputStream.writeInt(student.getScore());}dataOutputStream.flush();dataOutputStream.close();DataInputStream dataInputStream =new DataInputStream(new FileInputStream("data.dat"));for(int i = 0; i < students.length; i++) {String name = dataInputStream.readUTF();int score = dataInputStream.readInt();students[i] = new Student(name, score);students[i].showData();}dataInputStream.close();}catch(IOException e) {e.printStackTrace();}} }
這個程序在寫入檔案時,只提取對象的成員數據,而在讀出時將這些數據讀出,并將讀回的數據設定給一個實例,是對象數據還原的一種方式。
15、SequenceInputStream
您將一個檔案分割為數個檔案,接下來要將之再度組合還原為原來的檔案,最基本的作法是使用數個 FileInputStream來開啟分割后的檔案,然后一個一個檔案的讀取,并連續寫入至同一個FileOutputStream中,在這中間,您必須 要自行判斷每一個分割檔案的讀取是否完畢,如果完畢就換讀取下一個檔案。
如果您使用SequenceInputStream就不用這么麻煩,SequenceInputStream可以看作是數個 InputStream對象的組合,當一個InputStream對象的內容讀取完畢后,它就會取出下一個InputStream對象,直到所有的 InputStream對象都讀取完畢為止。
下面這個程序是SequenceInputStream的使用示范,它可以將指定的檔案進行分割,也可以將分割后的檔案還原為一個檔案:
SequenceStreamDemo.java
package onlyfun.caterpillar; import java.util.*; import java.io.*; public class SequenceStreamDemo {public static void main(String[] args) {try {// args[0]: 指定分割(s)或連接(c)switch (args[0].charAt(1)) {case 's':// args[1]: 每個分割檔案的大小int size = Integer.parseInt(args[1]);// args[2]: 指定要被分割的文件名稱seperate(args[2], size);break;case 'c':// args[1]: 指定要被組合的檔案個數int number = Integer.parseInt(args[1]);// args[2]: 組合后的文件名稱concatenate(args[2], number);break;}}catch(ArrayIndexOutOfBoundsException e) {System.out.println("Using: java UseSequenceStream [-s/-c]" +" (size/number) filename");System.out.println("-s: 分割檔案\n-c: 組合檔案");}catch(IOException e) {e.printStackTrace();}}// 分割檔案public static void seperate(String filename, int size)throws IOException {FileInputStream fileInputStream =new FileInputStream(new File(filename));BufferedInputStream bufInputStream =new BufferedInputStream(fileInputStream);byte[] data = new byte[1];int count = 0;?// 從原檔案大小及指定分割的大小// 決定要分割為幾個檔案if(fileInputStream.available() % size == 0)count = fileInputStream.available() / size;elsecount = fileInputStream.available() / size + 1;// 開始進行分割for(int i = 0; i < count; i++) {int num = 0;// 分割的檔案加上底線與編號File file = new File(filename + "_" + (i + 1));BufferedOutputStream bufOutputStream =new BufferedOutputStream(new FileOutputStream(file));while(bufInputStream.read(data) != -1) {bufOutputStream.write(data);num++;if(num == size) { // 分割出一個檔案bufOutputStream.flush();bufOutputStream.close();break;}}if(num < size) {bufOutputStream.flush();bufOutputStream.close();}}System.out.println("分割為" + count + "個檔案");}// 連接檔案public static void concatenate(String filename,int number) throws IOException {// 收集檔案用的ListList list =new ArrayList();for(int i = 0; i < number; i++) {// 文件名必須為底線加上編號File file = new File(filename + "_" + (i+1));list.add(i, new FileInputStream(file));}final Iterator iterator = list.iterator();// SequenceInputStream 需要一個Enumeration對象來建構Enumeration enumation =new Enumeration() {public boolean hasMoreElements() {return iterator.hasNext();}public InputStream nextElement() {return iterator.next();}};// 建立SequenceInputStream// 并使用BufferedInputStreamBufferedInputStream bufInputStream =new BufferedInputStream(new SequenceInputStream(enumation),8192);BufferedOutputStream bufOutputStream =new BufferedOutputStream(new FileOutputStream(filename), 8192);byte[] data = new byte[1];// 讀取所有檔案數據并寫入目的地檔案while(bufInputStream.read(data) != -1)bufOutputStream.write(data);bufInputStream.close();bufOutputStream.flush();bufOutputStream.close();System.out.println("組合" + number + "個檔案 OK!!");} }
分割檔案時的范例如下:
java onlyfun.caterpillar.SequenceStreamDemo -s 1048576 test.zip 分割為6個檔案
組合檔案時的范例如下:
java onlyfun.caterpillar.SequenceStreamDemo -c 6 test.zip 組合6個檔案 OK!!
16、PrintStream
之前所介紹過的Stream輸出對象,都是直接將內存中的數據寫出至目的地(例如一個檔案),舉個例子來說,如果您將 int 整數 1 使用之前介紹的Stream對象輸出至檔案,則檔案中所儲存的是 int 整數 1 在內存中的值,例如:
FileStream.java
package onlyfun.caterpillar; import java.io.*; public class FileStreamDemo {public static void main(String[] args)throws IOException {FileOutputStream file =new FileOutputStream(new File("test.txt"));file.write(1);file.close();} }
由于您使用write()方法,這會將 1 在內存中的值之低字節0000001寫入檔案中,所以如果您使用文字編輯軟件(像vi或UltraEdit)觀看test.txt的16進位表示,其結果會顯示 01(16進位表示)。
有時候您所想要儲存的結果是轉換為字符之后的結果,例如若程序的執行結果是3.14159,您會希望使用字符來儲存3.14159,也就是俗稱的儲存為純文本文件,如此當您使用簡單的純文字編輯器觀看時,就可以直接看到程序執行的結果。
例如您若想使用純文本文件看到test.txt的顯示結果是1,則必須先將內存中的整數1,也就是二進制00000000 00000000 00000000 00000001轉換為對應的字符編碼,也就是0x31(十進制表示49)并加以儲存。
使用PrintStream可以自動為您進行字符轉換的動作,它會使用操作系統的預設編碼來處理對應的字符轉換動作,直接使用下面這個例子來作示范:
PrintStreamDemo.java
package onlyfun.caterpillar; import java.io.*; public class PrintStreamDemo {public static void main(String[] args)throws FileNotFoundException {PrintStream printStream = new PrintStream(new FileOutputStream(new File("pi.txt")));printStream.print("PI = ");printStream.println(Math.PI);printStream.close();} }
執行程序之后使用純文字編輯器開啟pi.txt,其內容會是PI = 3.141592653589793,print()或println()接受int、char、String、double等等數據型態, println()會在輸出之后加上換行字符,而print()則不會。
注意在檔案儲存上實際并沒有二進制檔案或是純文本文件的分別,所有的檔案所儲存的都是二進制的數據,您俗稱的純文本文件,其實正確的說,是指儲存的結果是經過字符轉換,例如將 int 整數 1轉換為字符 '1' 的編碼結果并加以儲存。
17、Reader、Writer
Reader、Writer支持Unicode標準字符集(Character set)(字節串流則只支持ISO-Latin-1 8-bit),在處理串流時,會根據系統預設的字符編碼來進行字符轉換,它們是抽象類別,真正您會使用其子類別,子類別通常會重新定義相關的方法。
在 PushbackInputStream 中,您讀入一個含BIG5中文字及ASCII字符的文本文件,這邊改寫一下這個例子,使用Reader的子類別 InputStreamReader來轉換讀入的兩個字節為漢字字符,并顯示在屏幕上:
ReaderWriterDemo.java
package onlyfun.caterpillar; import java.io.*; public class ReaderDemo {public static void main(String[] args) {try {PushbackInputStream pushbackInputStream =new PushbackInputStream(new FileInputStream(args[0]));byte[] array = new byte[2];ByteArrayInputStream byteArrayStream =new ByteArrayInputStream(array);// reader會從已讀的位數組中取出數據InputStreamReader reader =new InputStreamReader(byteArrayStream);int tmp = 0;int count = 0;while((count = pushbackInputStream.read(array))!= -1) {// 兩個字節轉換為整數tmp = (short)((array[0] << 8) |(array[1] & 0xff));tmp = tmp & 0xFFFF;// 判斷是否為BIG5,如果是則顯示BIG5中文字if(tmp >= 0xA440 && tmp < 0xFFFF) {System.out.println("BIG5: " +(char)reader.read());// 重置ArrayInputStream的讀取光標// 下次reader才會再重頭讀取數據byteArrayStream.reset();}else {// 將第二個字節推回串流pushbackInputStream.unread(array, 1, 1);// 顯示ASCII范圍的字符System.out.println("ASCII: " +(char)array[0]);}}pushbackInputStream.close();}catch(ArrayIndexOutOfBoundsException e) {System.out.println("請指定文件名稱");}catch(IOException e) {e.printStackTrace();}} }
假設的文本文件中有以下的文字:"這T是e一s個t測試" ,執行結果會是:
BIG5: 這 ASCII: T BIG5: 是 ASCII: e BIG5: 一 ASCII: s BIG5: 個 ASCII: t BIG5: 測 BIG5: 試 ASCII: ! EOF?
InputStreamReader可以用字節串流中取出字節數據,并進行字符處理動作,關于Reader、Writer相關子類別,之后會于各相關主題進行介紹。
其余未看:
ByteArrayInputStream、ByteArrayOutputStream;
CharArrayReader、CharArrayWriter;
PushbackReader;
http://www.iteedu.com//plang/java/javadiary/92.php