|
1. Abstract: Java將I/O分為高階I/O與低階I/O,高階I/O在使用上提供更多的讀寫方法,如讀寫 int、double、String的資料型態(tài),而低階的I/O大部份只提供write、read的byte[]存取,因?yàn)槌淌酱蟛糠莸馁Y料都是以字串或 其它主要型態(tài)資料來運(yùn)算,因此低階的I/O在使用上不利於程式設(shè)計,所以Java將許多好用的方法全部集合成高階I/O; 換言之,低階I/O的主要工作是負(fù)責(zé)與媒體資料作存取,高階I/O類別主要作資料型態(tài)的轉(zhuǎn)換及提供一些特殊的功能。在使用Java I/O時要謹(jǐn)記的一個重要原則是,在建立一個I/O之前必需先用低階I/O類別來存取媒體資料(如檔案或pipe),之後再使用高階I/O來控制低階 I/O類別的動作,這種一層又一層的架構(gòu)稱I/O Chain。底下為Java的I/O架構(gòu)圖,第一個為以byte為單位的I/O,第二個則是以char為單位。 2. File I/O: A. FileInputStream & FileOutputStream FileInputStream是讀取檔案用的類別,其建構(gòu)式有叁個: public FileInputStream(String strFilename) throws FileNotFoundException public FileInputStream(File fIn) throws FileNotFoundException public FileInputStream(FileDescriptor fdObj) 在這里我只講第一個,這是最直覺的方式,如下的范例1,會一次從e:\test.txt讀10個bytes,將讀入的結(jié)果輸出到標(biāo)準(zhǔn)輸出設(shè)備,直到檔案結(jié) 束。在這個范例中要注意的是,available會傳回輸入串流中還有多少個bytes,read則會根據(jù)buffer的大小來決定一次讀幾個 bytes,并將實(shí)際讀到的byte數(shù)傳回。 ===== 范例 1 ===== import java.io.*; public class FIn { public FIn() { try { FileInputStream fis = new FileInputStream("e:/in.txt"); while (fis.available() > 0) { byte[] b = new byte[10]; int nResult = fis.read(b); if (nResult == -1) break; System.out.println(new String(b)); } fis.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { FIn fIn = new FIn(); } } FileOutputStream是寫入檔案用的類別,其建構(gòu)式有四個: Public FileOutputStream(String strFilename) throws FileNotFoundException Public FileOutputStream(File fOut) throws FileNotFound Exception Public FileOutputStream(FileDescriptor fdObj) public FileOutputStream(String name, boolean append) throws FileNotFoundException 第四個和第一個的差別只在於當(dāng)檔案存在時,第一個會將原來的檔案內(nèi)容覆蓋,第四個則可以選擇覆蓋或?qū)⑿聝?nèi)容接在原內(nèi)容後面。范例2以建構(gòu)式一講解如何寫入 一個檔案…在這個范例中要注意的是,fIn每個讀10個bytes,但是最後一次不一定會讀10個bytes,因此,fOut在write時,要指明要寫 幾個bytes到檔案中,否則最後一次仍會寫入10個bytes,因Java在new byte時會先將內(nèi)容先填0,所以後面的幾個bytes會是0。 ===== 范例2 ===== import java.io.*; public class FOut { public FOut() { try { FileInputStream fIn = new FileInputStream("e:/in.txt"); FileOutputStream fOut = new FileOutputStream("e:/out.txt"); while (fIn.available() > 0) { byte[] b = new byte[10]; int nResult = fIn.read(b); if (nResult == -1) break; fOut.write(b, 0, nResult); } fIn.close(); fOut.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { FOut FOut1 = new FOut(); } } B. FileReader & FileWriter FileReader 和FileInputStream最大不同在於,F(xiàn)ileInputStream讀取的單位是byte,F(xiàn)ileReader讀取的單位是char。另外 要注意的是,在FileInputStream中以available來判斷是否還有資料可讀取,在FileReader中是以ready來判斷, 但是,available是傳回還有多少個bytes可以讀取,ready則傳回true或false,當(dāng)傳回true時表示,下次read時保證不會停頓,當(dāng)傳回false時,表示下次read時”可能”停頓,所謂可能是指不保證不會停頓。 Ps. 測試時,in.txt里放些中文字就可以看出以byte和以char為單位有什麼不同。 ===== 范例 3 ===== import java.io.*; public class chFIn { public chFIn() { try { FileReader rdFile = new FileReader("e:/in.txt"); while (rdFile.ready()) { char[] chIn = new char[10]; int nResult = rdFile.read(chIn); if (nResult == -1) break; System.out.println(chIn); } rdFile.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { chFIn chFIn1 = new chFIn(); } } FileWriter和FileOutputStream的最大不同也在於寫入單位的不同,F(xiàn)ileOutputStream為byte,F(xiàn)ileWriter為char。 ===== 范例 4 ===== import java.io.*; public class chFOut { public chFOut() { try { FileReader rdFile = new FileReader("e:/in.txt"); FileWriter wrFile = new FileWriter("e:/out.txt"); while (rdFile.ready()) { char[] chIn = new char[10]; int nResult = rdFile.read(chIn); if (nResult == -1) break; wrFile.write(chIn, 0, nResult); } rdFile.close(); wrFile.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { chFOut chFOut1 = new chFOut(); } } C. BufferedReader & BufferedWriter File I/O是相當(dāng)耗時的作業(yè),通常電腦在做處理時,者會建立一個緩沖區(qū),一次讀取或?qū)懭胍粋€區(qū)塊,借由減少I/O次數(shù),來節(jié)省時間 ,在Java中的BufferedReader和BufferedWriter就是提供這樣的緩沖功能。 在 范例5中,我們將FileReader導(dǎo)向BufferedReader,將FileWriter導(dǎo)向BufferedWriter,來達(dá)到區(qū)塊讀取、寫 入的目的。BufferedReader提供的readLine一次可以讀取一行,當(dāng)遇到檔尾時,會傳回null。BufferedWriter提供的 newLine會產(chǎn)生列尾符號,這個列尾符號隨作業(yè)系統(tǒng)的不同而不同,在Windows上為\r\n,在Unix上為\n,在Mac上為\r,這個符號是 依據(jù)line.separator系統(tǒng)性質(zhì)而來的。需注意的是,如果將BufferedWriter應(yīng)用到網(wǎng)路程式時,絕對不要使用newLine,因?yàn)?絕大多數(shù)的網(wǎng)路協(xié)定都是以\r\n為列尾,不會因作業(yè)系統(tǒng)不同而異。 ===== 范例 5 ===== import java.io.*; public class bufIn { public bufIn() { try { FileReader rdFile = new FileReader("e:/in.txt"); BufferedReader brdFile = new BufferedReader(rdFile); FileWriter wrFile = new FileWriter("e:/out.txt"); BufferedWriter bwrFile = new BufferedWriter(wrFile); String strLine; while ((strLine = brdFile.readLine()) != null) { bwrFile.write(strLine); bwrFile.newLine(); } brdFile.close(); bwrFile.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { bufIn bufIn1 = new bufIn(); } } D. File 在檔案處理方面,程式不只是要對檔案做讀、寫,有時也需要得知檔案的屬性,或刪除、移動、更名,有時則是要找出或列出某目錄下的某些檔案,針對這些運(yùn)作,Java提供了File這個類別。底下的范例,說明如何使用File類別。 a. 如何得知檔案屬性: 在 范例6中需注意的是lastModified傳回的最後更改時間是自1970/1/1 00:00:00算起的時間,單位為毫秒,所以要用Date將它轉(zhuǎn)換成日期、時間; 另外getCanonicalPath和getAbsolutePath得到的值在Windows上會是一樣的,在Unix可能就會不一樣。 ===== 范例 6 ===== import java.io.*; import java.util.*; public class FileSpy { public FileSpy(String strFilename) { File fFile = new File(strFilename); if (fFile.exists()) { System.out.println("Name: " + fFile.getName()); System.out.println("Absolute path: " + fFile.getAbsolutePath()); try { System.out.println("Canonical path: " + fFile.getCanonicalPath()); } catch (IOException e) { e.printStackTrace(); } if (fFile.canWrite()) System.out.println(fFile.getName() + " is writable"); if (fFile.canRead()) System.out.println(fFile.getName() + " is readable"); if (fFile.isFile()) { System.out.println(fFile.getName() + " is a file"); } else if (fFile.isDirectory()) { System.out.println(fFile.getName() + " is a directory"); } else { System.out.println("What is this?"); } long lngMilliseconds = fFile.lastModified(); if (lngMilliseconds !=0) System.out.println("last modified at " + new Date(lngMilliseconds)); long lngLen = fFile.length(); if (lngLen !=0) System.out.println("size: " + lngLen); } else System.out.println("file not found"); } public static void main(String[] args) { if (args.length == 1) { FileSpy fileSpy1 = new FileSpy(args[0]); } else System.out.println("Usage: java FileSpy Filename"); } } b. 建立、刪除、移動、更名: File 類別提供了createNewFile、renameTo、delete作為建立(createNewFile)、刪除(delete)、移動、更名 (renameTo)之用,使用方式如下: (移動和更名都用renameTo,就如在Unix上檔案搬移和更名都用mv一樣) ===== 范例 7 ===== import java.io.*; public class OperateFile { public OperateFile() { //create new file File fNewFile = new File("C:/newfile.txt"); try { if (fNewFile.exists() == false) { if (fNewFile.createNewFile() == true) { System.out.println("create c:/newfile.txt success"); } else { System.out.println("create c:/newfile.txt fail"); } } else { System.out.println("file already exists"); } } catch (IOException e) { e.printStackTrace(); } //rename file File fRenameFile = new File("c:/renamefile.txt"); fNewFile.renameTo(fRenameFile); //remove file File fRemoveFile = new File("d:/" + fRenameFile.getName()); fRenameFile.renameTo(fRemoveFile); //delete file try { File fDelFile = new File(fRemoveFile.getCanonicalPath()); fDelFile.delete(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { OperateFile operateFile1 = new OperateFile(); } } c. 找出某特定目錄里的所有檔案: File類別提供的list和listFiles都可以列出某特定目錄里的所有檔案,其中l(wèi)ist傳回的是String[],listFiles傳回的是File[],這兩個函式都會傳回所有的檔案和目錄。 ===== 范例 8 ===== import java.io.*; public class ListAllFiles { public ListAllFiles(String strDir) { File fDir = new File(strDir); File[] fAllFiles = fDir.listFiles(); for(int i=0; i if (fAllFiles.isFile()) System.out.println("File: " + fAllFiles.getName()); else System.out.println("Dir: " + fAllFiles.getName()); } } public static void main(String[] args) { ListAllFiles listAllFiles1 = new ListAllFiles(args[0]); } } 3. Network I/O: Java對網(wǎng)路的支援只有TCP/IP和UDP/IP,提供的類別有URL、URLConnection、Socket、ServerSocket,在這里我只打算用ServerSocket、Socket為例,來說明Network I/O。 基本上,Java的I/O不管在任何的媒體上都是將它們視為stream,所以,網(wǎng)路I/O和檔案I/O原理也是一致的,底下的兩個程式分別為server socket及client socket。在看范例之前,可以再復(fù)習(xí)一下前面的abstract… ===== 范例 9 ===== import java.net.*; import java.io.*; public class myServer { public myServer(String strPort) { int nPort = new Integer(strPort).intValue(); try { ServerSocket ss = new ServerSocket(nPort); Socket s = ss.accept(); OutputStream out = s.getOutputStream(); PrintStream psOut = new PrintStream(out); String strResponse = "Hello " + s.getInetAddress() + " on port " + s.getPort() + "\r\n"; strResponse += "This is " + s.getLocalAddress() + " on port " + s.getLocalPort() + "\r\n"; psOut.print(strResponse); s.close(); ss.close(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { myServer myServer1 = new myServer(args[0]); } } ===== 范例 10 ===== import java.net.*; import java.io.*; public class myClient { public myClient(String strIP, String strPort) { int nPort = new Integer(strPort).intValue(); try { Socket s = new Socket(strIP, nPort); InputStream in = s.getInputStream(); BufferedInputStream bisIn = new BufferedInputStream(in); while (bisIn.available() > 0) { byte[] b = new byte[30]; int nLen = bisIn.read(b); System.out.println(new String(b, 0, nLen)); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { myClient myClient1 = new myClient(args[0], args[1]); } } 4. Object Serialization: A. 所謂Object Serialization就是把物件的”狀態(tài)”儲存成一系列的位元組,而這些位元組在稍候可用來恢復(fù)物件。更簡單的說,Object Serialization是讓物件可以以物件為儲存單位。在Java中,任何物件要能Serialization,必須implements Serializable這個Interface,以下是一個簡單的程式范例,可以將物件儲存到e:\point.ser,或從e:\point.ser 將物件恢復(fù)原值。 ===== 范例 11 ===== import java.io.*; public class ThreeDPoint implements Serializable { private double m_dblX, m_dblY, m_dblZ; public ThreeDPoint(double x, double y, double z) { m_dblX = x; m_dblY = y; m_dblZ = z; } public void PrintXYZ() { System.out.println("X: " + m_dblX); System.out.println("Y: " + m_dblY); System.out.println("Z: " + m_dblZ); } public static void main(String[] args) { if (args[0].equalsIgnoreCase("w")) { ThreeDPoint threeDPoint1 = new ThreeDPoint(10 ,20, 30); try { FileOutputStream fout = new FileOutputStream("e:\\point.ser"); ObjectOutputStream oout = new ObjectOutputStream(fout); oout.writeObject(threeDPoint1); oout.close(); System.out.println("write:"); threeDPoint1.PrintXYZ(); } catch (Exception e) { e.printStackTrace(); } } else if (args[0].equalsIgnoreCase("r")) { try { FileInputStream fin = new FileInputStream("e:\\point.ser"); ObjectInputStream oin = new ObjectInputStream(fin); Object o = oin.readObject(); ThreeDPoint threeDPoint1 = (ThreeDPoint) o; oin.close(); System.out.println("read:"); threeDPoint1.PrintXYZ(); } catch (Exception e) { } } } //end of main } B. 在Java中,一個實(shí)作某特定介面的類別,其子類別也因繼承的原故而被視為實(shí)作了該介面,因此,許多沒有明確宣告實(shí)作Serializable介面的類別,事實(shí)上也是可以被Serialization的。 C. 并非每個實(shí)作了Serializable介面的物件都可以被Serialization,如果這個物件繼承圖上的祖先,有其中一個是不可以被Serialization,那麼這個物件就不可以被Serialization。 5. Formated I/O: 在Java 的I/O里,并沒有所謂的型別,不管是int、long、double…最後都是以String輸出,所以如果要讓數(shù)字以特定格式輸出,需透過Java提 供的兩個類別java.text.NumberFormat和java.text.DecimalFormat將數(shù)字格式化後再輸出。 范例12簡 要說明NumberFormat如何使用,在開始使用NumberFormat時,應(yīng)先用getInstance取得NumberFormat的實(shí)體,范 例12中的setMaximumIntegerDigits和setMinimumFractionDigits是用來設(shè)定整數(shù)和小數(shù)的位數(shù),另外還有 setMinimumIntegerDigits和setMaximumFractionDigits也是同樣功能。這些設(shè)定如有沖突,Java以最後設(shè) 定的為準(zhǔn)。 ===== 范例 12 ===== import java.text.*; public class myFormat { public myFormat() { NumberFormat nf = NumberFormat.getInstance(); double dblNum = Math.PI; System.out.println(dblNum); nf.setMaximumIntegerDigits(5); nf.setMinimumFractionDigits(4); System.out.println("PI: " + nf.format(dblNum)); } public static void main(String[] args) { myFormat myFormat1 = new myFormat(); } } 另 一個類別DecimalFormat是繼承NumberFormat的子類別,它提供了更強(qiáng)的格式化功能,透過設(shè)定pattern,可以使我們的輸出更多 樣化,至於Java提供的pattern有那些? 在API Document中有詳細(xì)說明! 范例13僅舉其一,說明DecimalFormat如何使用。 ===== 范例 13 ===== import java.text.*; public class myDecimalFormat { public myDecimalFormat() { DecimalFormat df = new DecimalFormat("0000.000"); double dblNum = 123.45; System.out.println("dblNum: " + dblNum); System.out.println("dblNum: " + df.format(dblNum)); } public static void main(String[] args) { myDecimalFormat myDecimalFormat1 = new myDecimalFormat(); } } |
|
|