java IO

Java I/O

(Steven Shi, idealist@gcn.net.tw, 2002/3/31)

 

1.     Abstract:

JavaI/O分為高階I/O與低階I/O,高階I/O在使用上提供更多的讀寫方法,如讀寫intdoubleString的資料型態,而低階的I/O大部份只提供writereadbyte[]存取,因為程式大部份的資料都是以字串或其它主要型態資料來運算,因此低階的I/O在使用上不利於程式設計,所以Java將許多好用的方法全部集合成高階I/O; 換言之,低階I/O的主要工作是負責與媒體資料作存取,高階I/O類別主要作資料型態的轉換及提供一些特殊的功能。在使用Java I/O時要謹記的一個重要原則是,在建立一個I/O之前必需先用低階I/O類別來存取媒體資料(如檔案或pipe),之後再使用高階I/O來控制低階I/O類別的動作,這種一層又一層的架構稱I/O Chain

底下為JavaI/O架構圖,第一個為以byte為單位的I/O,第二個則是以char為單位。

 

2.     File I/O:

A.     FileInputStream & FileOutputStream

FileInputStream是讀取檔案用的類別,其建構式有三個:

public FileInputStream(String strFilename) throws FileNotFoundException

public FileInputStream(File fIn) throws FileNotFoundException

public FileInputStream(FileDescriptor fdObj)

   在這裡我只講第一個,這是最直覺的方式,如下的範例1,會一次從e:/test.txt10bytes,將讀入的結果輸出到標準輸出設備,直到檔案結束。在這個範例中要注意的是,available會傳回輸入串流中還有多少個bytesread則會根據buffer的大小來決定一次讀幾個bytes,並將實際讀到的byte數傳回。

===== 範例 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是寫入檔案用的類別,其建構式有四個:

                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

 

        第四個和第一個的差別只在於當檔案存在時,第一個會將原來的檔案內容覆蓋,第四個則可以選擇覆蓋或將新內容接在原內容後面。範例2以建構式一講解如何寫入一個檔案…在這個範例中要注意的是,fIn每個讀10bytes,但是最後一次不一定會讀10bytes,因此,fOutwrite時,要指明要寫幾個bytes到檔案中,否則最後一次仍會寫入10bytes,因Javanew byte時會先將內容先填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

FileReaderFileInputStream最大不同在於,FileInputStream讀取的單位是byteFileReader讀取的單位是char。另外要注意的是,在FileInputStream中以available來判斷是否還有資料可讀取,在FileReader中是以ready來判斷,

但是,available是傳回還有多少個bytes可以讀取,ready則傳回truefalse,當傳回true時表示,下次read時保證不會停頓,當傳回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();

  }

}

        FileWriterFileOutputStream的最大不同也在於寫入單位的不同,FileOutputStreambyteFileWriterchar

===== 範例 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是相當耗時的作業,通常電腦在做處理時,者會建立一個緩衝區,一次讀取或寫入一個區塊,借由減少I/O次數,來節省時間 ,在Java中的BufferedReaderBufferedWriter就是提供這樣的緩衝功能。

在範例5中,我們將FileReader導向BufferedReader,將FileWriter導向BufferedWriter,來達到區塊讀取、寫入的目的。BufferedReader提供的readLine一次可以讀取一行,當遇到檔尾時,會傳回nullBufferedWriter提供的newLine會產生列尾符號,這個列尾符號隨作業系統的不同而不同,在Windows上為/r/n,在Unix上為/n,在Mac上為/r,這個符號是依據line.separator系統性質而來的。需注意的是,如果將BufferedWriter應用到網路程式時,絕對不要使用newLine,因為絕大多數的網路協定都是以/r/n為列尾,不會因作業系統不同而異。

===== 範例 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

在檔案處理方面,程式不只是要對檔案做讀、寫,有時也需要得知檔案的屬性,或刪除、移動、更名,有時則是要找出或列出某目錄下的某些檔案,針對這些運作,Java提供了File這個類別。底下的範例,說明如何使用File類別。

a.       如何得知檔案屬性:

在範例6中需注意的是lastModified傳回的最後更改時間是自1970/1/1 00:00:00算起的時間,單位為毫秒,所以要用Date將它轉換成日期、時間; 另外getCanonicalPathgetAbsolutePath得到的值在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類別提供了createNewFilerenameTodelete作為建立(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類別提供的listlistFiles都可以列出某特定目錄裡的所有檔案,其中list傳回的是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<fAllFiles.length; i++) {

      if (fAllFiles[i].isFile())

        System.out.println("File: " + fAllFiles[i].getName());

      else

        System.out.println("Dir: " + fAllFiles[i].getName());

    }

  }

  public static void main(String[] args) {

    ListAllFiles listAllFiles1 = new ListAllFiles(args[0]);

  }

}

 

 

3.     Network I/O:

Java對網路的支援只有TCP/IPUDP/IP,提供的類別有URLURLConnectionSocketServerSocket,在這裡我只打算用ServerSocketSocket為例,來說明Network I/O

基本上,JavaI/O不管在任何的媒體上都是將它們視為stream,所以,網路I/O和檔案I/O原理也是一致的,底下的兩個程式分別為server socketclient socket。在看範例之前,可以再複習一下前面的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就是把物件的狀態儲存成一系列的位元組,而這些位元組在稍候可用來恢復物件。更簡單的說,Object Serialization是讓物件可以以物件為儲存單位。在Java中,任何物件要能Serialization,必須implements Serializable這個Interface,以下是一個簡單的程式範例,可以將物件儲存到e:/point.ser,或從e:/point.ser將物件恢復原值。

===== 範例 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中,一個實作某特定介面的類別,其子類別也因繼承的原故而被視為實作了該介面,因此,許多沒有明確宣告實作Serializable介面的類別,事實上也是可以被Serialization的。

C.     並非每個實作了Serializable介面的物件都可以被Serialization,如果這個物件繼承圖上的祖先,有其中一個是不可以被Serialization,那麼這個物件就不可以被Serialization

 

5.     Formated I/O:

JavaI/O裡,並沒有所謂的型別,不管是intlongdouble…最後都是以String輸出,所以如果要讓數字以特定格式輸出,需透過Java提供的兩個類別java.text.NumberFormatjava.text.DecimalFormat將數字格式化後再輸出。

範例12簡要說明NumberFormat如何使用,在開始使用NumberFormat時,應先用getInstance取得NumberFormat的實體,範例12中的setMaximumIntegerDigitssetMinimumFractionDigits是用來設定整數和小數的位數,另外還有setMinimumIntegerDigitssetMaximumFractionDigits也是同樣功能。這些設定如有衝突,Java以最後設定的為準。

===== 範例 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的子類別,它提供了更強的格式化功能,透過設定pattern,可以使我們的輸出更多樣化,至於Java提供的pattern有那些? API Document中有詳細說明! 範例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();

  }

}

 

6.     Reference:

A.     Java實用程式設計,李昇暾、詹智安著,松崗出版。

B.     Java I/O技術,Elliotte Rusty Harold著,陳建宏、張偉皓、林長毅譯,O’REILLY出版。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值