Java學習筆記(7)——輸入輸出

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

轉載于:https://www.cnblogs.com/CheeseZH/archive/2012/12/10/2811913.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/274445.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/274445.shtml
英文地址,請注明出處:http://en.pswp.cn/news/274445.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

全庫模式 用戶模式 表模式_暗模式,亮模式和用戶的故事

全庫模式 用戶模式 表模式I have been working on designing a UI for an app that has individuals over the age of 60 as its main audience. At some point, I found my design more appealing in dark mode. As a UX designer, I know that my opinions and preferences d…

Rollup 與 Webpack 的 Tree-shaking

大家好&#xff0c;我是若川。我持續組織了近一年的源碼共讀活動&#xff0c;感興趣的可以 點此掃碼加我微信 lxchuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試…

聚類與分類的主要區別在于:_經驗在于細節:分析流服務的用戶體驗

聚類與分類的主要區別在于&#xff1a;看不見的差異 (The Invisible Difference) When app markets mature the overlap in features and designs grows closer as they catch up and copy each other. The more similar the apps are to one another, the more important the …

asp.net 動態創建TextBox控件 如何加載狀態信息

接著上文Asp.net TextBox的TextChanged事件你真的清楚嗎&#xff1f; 這里我們來說說狀態數據時如何加載的。雖然在Control中有調用狀態轉存的方法&#xff0c;但是這里有一個判斷條件 if (_controlState > ControlState.ViewStateLoaded) 一般的get請求這里的條件是不滿足…

從零實現一個迷你 Webpack

大家好&#xff0c;我是若川。我持續組織了近一年的源碼共讀活動&#xff0c;感興趣的可以 點此掃碼加我微信 lxchuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試…

ios 刷新遮罩遮罩_在Adobe XD中進行遮罩的3種方法

ios 刷新遮罩遮罩Are you new to Adobe XD? Or maybe you’re just stuck on how to create a simple mask? Here are 3 quick tips for how to mask your photos and designs in Adobe XD.您是Adobe XD的新手嗎&#xff1f; 或者&#xff0c;也許您只是停留在如何創建簡單的…

C#除法運算

C#中進行算術運算容易牽扯到類型的自動轉換&#xff0c;這種自動轉換稱之為隱式轉換&#xff0c;當然還可以人為的強制轉換 隱式轉換要求&#xff1a;不丟失精度&#xff0c;而且轉換前后都為數值 強制轉換&#xff1a;容易丟失可能會丟失精度 1 namespace 除法運算2 {3 cl…

Vite 4.0 正式發布!

源碼共讀我新出了&#xff1a;第40期 | vite 是如何解析用戶配置的 .env 的鏈接&#xff1a;https://www.yuque.com/ruochuan12/notice/p40也可以點擊文末閱讀原文查看&#xff0c;歡迎學習記筆記~12 月 9 日&#xff0c;Vite 4.0 正式發布。下面就來看看 Vite 4.0 有哪些更新吧…

代碼復審

我們CodingCook復審的是WWW的代碼&#xff0c;他們的項目是時間管理助手&#xff08;TimeLine&#xff09;。只是跟根據自己的經驗來看&#xff0c;不一定準 先說一下整體的感覺。WWW的代碼用了應該是比較符合面向對象的思想&#xff0c;借口&#xff0c;封裝隨處可見&#xff…

圖像標注技巧_保護互聯網上圖像的一個簡單技巧

圖像標注技巧補習 (TUTORIAL) Have you ever worried about sharing your images on the Internet? Anytime you upload something to the web you risk the chance of your work being used (without permission) by another.您是否曾經擔心過要在Internet上共享圖像&#xf…

【VueConf 2022】尤雨溪:Vue的進化歷程

大家好&#xff0c;我是若川。我持續組織了近一年的源碼共讀活動&#xff0c;感興趣的可以 點此掃碼加我微信 lxchuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試…

WCF netTcpBinding寄宿到IIS7

config配置文件不多說 <?xml version"1.0" encoding"utf-8" ?> <configuration><system.serviceModel><behaviors><serviceBehaviors><behavior name"myBehavior"><serviceMetadata/></behavior…

ar軟件測試工具_如何為用戶測試制作快速的AR原型

ar軟件測試工具We had a project recently with an element of AR-based interaction, which it turned out was impossible to create as a prototype in either Invision or Framer (our current stack). This had a massive impact on our ability to test with users in a …

未來ui設計的發展趨勢_2025年的未來UI趨勢?

未來ui設計的發展趨勢Humans are restless.人類是不安的。 Once we find something that works, we get used to it and we crave the next big thing. The next innovation. When will the future finally arrive? And when it does, how long will it take us to get used …

內存泄露檢測 vld

VLD是一款開源檢測內存泄露軟件的簡稱&#xff1a;Visual Leak Detector 網站&#xff1a;http://vld.codeplex.com/ 使用&#xff1a; 1. 安裝vld 或者 下載相關 .h&#xff0c;lib&#xff0c;dll 文件 2. 方法很簡單&#xff0c;只要在包含入口函數的.cpp文件中包含vld.h就可…

Monorepo 在網易的工程改造實踐

大家好&#xff0c;我是若川。我持續組織了近一年的源碼共讀活動&#xff0c;感興趣的可以 點此掃碼加我微信 lxchuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試…

這一年,Vue.js 生態開源之旅帶給我很大收獲~

大家好&#xff0c;我是若川。我持續組織了近一年的源碼共讀活動&#xff0c;感興趣的可以 點此掃碼加我微信 lxchuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試…

CSSyphus:煩躁不安的煩惱設計指南。

I’m trapped at home with my website. Or maybe it’s trapped at home with me. While some are using the weird lump of time provided by lockdown to indulge in baking, dancing, painting, singing, I’m using it to play around with code.我 被自己的網站困在家里。…

重構與臭豆腐4

重構要繼續&#xff0c;臭豆腐要做。   這個重構中各種提取類&#xff0c;方法&#xff0c;字段&#xff0c;可以方便的理解&#xff0c;如果使用了設置模式就更加邏輯清晰了。切東西也要講究刀法的。 重構可以方便的使用設計模式。設計模式為重構提供了目標。 比如多個if 可…

你構建的代碼為什么這么大?如何優化~

大家好&#xff0c;我是若川。我持續組織了近一年的源碼共讀活動&#xff0c;感興趣的可以 點此掃碼加我微信 lxchuan12 參與&#xff0c;每周大家一起學習200行左右的源碼&#xff0c;共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20余篇源碼文章。歷史面試…