JAVA : I/O系統

*

java.io

java的核心庫java.io提供了全面的IO接口。包括:文件讀寫、標準設備輸出等。Java中IO是以流爲基礎進行輸入輸出的,所有數據被串行化寫入輸出流,或者從輸入流讀入。

通過數據流、序列化和文件系統提供系統輸入和輸出。


流是一個很形象的概念,當程序需要讀取數據的時候,就會開啓一個通向數據源的流,這個數據源可以是文件,內存,或是網絡連接。類似的,當程序需要寫入數據的時候,就會開啓一個通向目的地的流。這時候你就可以想象數據好像在這其中“流”動一樣。


java.io常用類

JDK所提供的所有流類位於java.io包中,都分別繼承自以下四種抽象流類。
InputStream:繼承自InputStream的流都是用於向程序中輸入數據的,且數據單位都是字節(8位)。
OutputStream:繼承自OutputStream的流都是程序用於向外輸出數據的,且數據單位都是字節(8位)。
Reader:繼承自Reader的流都是用於向程序中輸入數據的,且數據單位都是字符(16位)。
Writer:繼承自Writer的流都是程序用於向外輸出數據的,且數據單位都是字符(16位)。


Java流輸入輸出原理

Java把這些不同來源和目標的數據都統一抽象爲數據流。Java語言的輸入輸出功能是十分強大而靈活的,美中不足的是看上去輸入輸出的代碼並不是很簡潔,因爲你往往需要包裝許多不同的對象。
在Java類庫中,IO部分的內容是很龐大的,因爲它涉及的領域很廣泛:標準輸入輸出,文件的操作,網絡上的數據流,字符串流,對象流,zip文件流。


File類

在Java中,可以用File類來表示一個文件,文件可以是所有文件,包括文件夾.(可以理解爲linux中的文件概念,一切皆文件,File)

目錄列表器

public class Demo {
    public static FilenameFilter filter (final String regex) {//傳入的參數必須是final的,這樣它才能夠使用來自該類範圍之外的對象
        return new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.equals("迅雷下載");//匿名內部類的實現方式
            }
        };
    }
    public static void main(String[] args) {
        File file = new File("d:\\");
        if (file.isDirectory()) {
            System.out.println(file.getPath());//查看路徑
            String[] files = file.list(new MyFileNmeFilter());//也可以不指定過濾器
            for (String s : files) {
                System.out.println(s);//查看目錄內所有文件
            }
        }
    }
}

class MyFileNmeFilter implements FilenameFilter {//當然也可以用匿名內部類來實現
    @Override
    public boolean accept(File dir, String name) {
        return name.equals("迅雷下載");//可用正則表達式過濾
    }
}

一個簡單的查看當前目錄文件的遞歸

當查看C D 等位置的時候,可能會報空指針。這段代碼會查看當前目錄下的所有文件。

public class Demo {
    public static void fun(File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                fun(f);
            }
        } else {
            System.out.println(file);
        }
    }

    public static void main(String[] args) {
        String str = ".";//查看當前目錄
        File f = new File(str);
        fun(f);
    }
}

輸入和輸出


流概念

首先要講一下”流”這個概念;編程語言的I/O庫中常使用流這個抽象概念,它代表任何有能力產出數據的數據源對象或有能力接收數據的接收端對象。”流”屏蔽了實際的I/O設備處理數據的細節。然後之所以下面這些IO類叫做經典的輸入輸出流,是因爲後面還會接觸到Java的NIO。

什麼是IO

Java中I/O操作主要是指使用Java進行輸入,輸出操作. Java所有的I/O機制都是基於數據流進行輸入輸出,這些數據流表示了字符或者字節數據的流動序列。Java的I/O流提供了讀寫數據的標準方法。任何Java中表示數據源的對象都會提供以數據流的方式讀寫它的數據的方法。

Java.io是大多數面向數據流的輸入/輸出類的主要軟件包。此外,Java也對塊傳輸提供支持,在覈心庫 java.nio中採用的便是塊IO。

流IO的好處是簡單易用,缺點是效率較低。塊IO效率很高,但編程比較複雜。

Java IO模型 :
Java的IO模型設計非常優秀,它使用Decorator模式,按功能劃分Stream,您可以動態裝配這些Stream,以便獲得您需要的功能。例如,您需要一個具有緩衝的文件輸入流,則應當組合使用FileInputStream和BufferedInputStream。
首先來講我們需要從幾個方面來對Java的IO進行一個分類,以便有一個直觀的認識:
1. 輸入流和輸出流
輸入流是指只能從中讀數據,而不能向裏面寫數據:包括InputStream和Reader兩個基本接口
輸出流是指只能向裏面寫數據而不能向其中讀數據:包括OutputStream和Writer兩個基本接口

  1. 字節流和字符流
    字節流和字符流的唯一不同就在於他們的操作的數據單元不同而已,字節流操作的數據單元是字節,而字符流操作的數據單元是字符。所有從Writer,Reader派生來的類都是字符流的類。而所有從InputStream和OutputStream派生出來的類都是字節流的類。

  2. 節點流和處理流
    所謂的節點流指的是:可以向/從一個特定的IO設備(如磁盤,網絡,數組,字符串)讀/寫的流;節點流也被成爲低級流。
    所謂的處理流指的是:對一個已存在的流進行連接或封裝,然後通過封裝以後的流來實現數據的讀寫功能。處理流也叫做高級流。
    Java中的IO使用了裝飾器的設計模式,就是體現在節點流和處理流的包裝關係上。使用處理流的好處在於我們可以忽略掉底層數據源的不同,而使用統一的方式進行讀寫。
    這裏寫圖片描述

因爲節點流是所有輸入輸出的基礎,所以有必要拿出來單獨細看一下。
(1) . InputStream 對應的節點流(按對應的數據源分) ,作用是用來表示那些從不同數據源產生輸入的類。
1. 字節數組:ByteArrayInputStream。以字節數組作爲讀取對象,允許將內存的緩衝區當作InputStream使用。
2. String對象:StringBufferInputSteam。將String轉換成InputStream,過期了
3. 文件:FileInputStream。用於從文件中讀取信息。
4. ”管道”: PipedInputStream,產生用於寫入相關PipedOutputStream的數據,實現管道化“概念”。
5. FilterInputStream 抽象類,作爲“裝飾器”的接口,其中,裝飾器爲其他的InputStream 類提供有用功能。

(2).OutputSteamd對應的結點流 ,該類別的類決定了輸出所要去往的目標。
1. ByteArrayOutputStream。在內存中創建緩衝區,所有送往”流”的數據都要放置在此緩衝區。
2. FileOutputStream 用於將信息寫入到文件
3. PipedOutputStream 將信息寫入到管道
4. FilterOutputStream 抽象類,作爲“裝飾器”的接口,其中,裝飾器爲其他的OutputSteamd類提供有用功能。

(3).Reader對應的節點流
1. FileReader:以文件作爲讀取對象
2. StringReader:以字符串作爲讀取對象
3. CharArrayReader:以字符數組作爲讀取對象
4. PipeReader:以管道作爲讀取對象

(4).Writer對應的節點流
1. FileReader:以文件作爲輸出對象
2. StringWriter:以字符串作爲輸出對象
3. CharArrayReader:以字符數組作爲輸出對象
4. PipedWriter:以管道作爲輸出對象

InputStream可以通過InputStreamReader轉換成Reader的,而OutPutstream可以通過OutputStreamWriter轉換成Writer。


代碼舉例

. I/O流 包裝,作用於讀文件

public class Demo {
    public static void main(String[] args) throws Exception {
        File file = new File("./test.txt");//指定輸入文件
        if(file.exists()){
            System.out.println("file exists,begin read file...");
            try {
                FileInputStream fis = new FileInputStream(file);//文件流作爲輸入
                InputStreamReader isr = new InputStreamReader(fis,"UTF-8");//轉換爲字符輸入流
                BufferedReader br = new BufferedReader(isr);//轉換爲緩衝流,提高速度,這是非常常見的方式
                String line;
                while((line = br.readLine()) != null){
                    System.out.println(line);
                }
                br.close();
                isr.close();
                fis.close();
                System.out.println("read file end...");
            } catch (final Exception e) {
                e.printStackTrace();
            }
        }
    }
}

. 讀取 >> 寫入

public class Demo {
    private static final String file = "./test.txt";//讀取的文件

    public static void main(String[] args) throws Exception {
        int LineNum = 1;
        BufferedReader br = new BufferedReader(new FileReader(file));

        PrintWriter pw = new PrintWriter("/writertest.txt");//要被寫入的文件
        //PrintWriter中提供了一個快捷的構造器方式,可以使得我們不用進行復雜的包裝

        String s ;
        while ((s = br.readLine()) != null) {
            pw.println(LineNum++ +": "+s);
        }
        pw.close();
        br.close();
    }
}

. DataOutputStream
如果我們使用DataOutputStream寫入數據,那麼java就可以保證我們使用DataInputStream準確的讀取數據,無論讀和寫的平臺多麼不同,

public class Demo {
    private static final String file = "./test.txt";//讀取的文件

    public static void main(String[] args) throws Exception {

        //使用DataOutputStream向文件寫入數據
        DataOutputStream out =new DataOutputStream(new BufferedOutputStream(new FileOutputStream("./dataFile.txt")));

        ///當我們使用DataOutputStream寫字符串並讓DataInputStream能夠恢復
        //它的唯一可靠做法就是使用UTF-8編碼,這裏是使用writeUTF()和readUTF()實現的
        out.writeUTF("That was a message");//此處可寫入不同的數據類型
        out.close();

        DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream("./dataFile.txt")));
        System.out.println(dis.readUTF());
        dis.close();
    }
}

. 使用DataInputStream逐個讀取字節

public class Demo {
    private static final String file = "./test.txt";//讀取的文件

    public static void main(String[] args) throws Exception {
        DataInputStream dis = new DataInputStream(new ByteArrayInputStream("kkkkkkkkk".getBytes()));
        while(dis.available()!=0){
            System.out.print((char)dis.readByte());
        }
    }
}

. 管道流PipedInputStream和PipedOutputStream
管道流內部在實現時還有大量的對同步數據的處理 ,管道輸出流和管道輸入流執行時不能互相阻塞,所以一般要開啓獨立線程分別執行 ,順便複習了多線程操作

public class Demo {

    public static void main(String[] args) throws Exception {
        PipedInputStream pis = new PipedInputStream();
        PipedOutputStream pos = new PipedOutputStream();
        pis.connect(pos);//輸入流與輸出流連接
        ReadThread readThread = new ReadThread(pis);
        WriteThread writeThread = new WriteThread(pos);
        new Thread(readThread).start();
        new Thread(writeThread).start();

    }
}

class ReadThread implements Runnable {

    private PipedInputStream pis;

    ReadThread(PipedInputStream pis) {
        this.pis = pis;
    }

    @Override
    public void run() {//由於必須要覆蓋run方法,所以這裏不能拋,只能try
        try {
            sop("R:讀取前沒有數據,阻塞中...等待數據傳過來再輸出到控制檯...");
            byte[] buf = new byte[1024];
            int len = pis.read(buf);  //read阻塞
            sop("R:讀取數據成功,阻塞解除...");

            String s = new String(buf, 0, len);
            sop(s);    //將讀取的數據流用字符串以字符串打印出來
            pis.close();
        } catch (Exception e) {
            throw new RuntimeException("R:管道讀取流失敗!");
        }
    }

    private static void sop(Object obj) //打印
    {
        System.out.println(obj);
    }
}

class WriteThread implements Runnable {

    private PipedOutputStream pos;

    WriteThread(PipedOutputStream pos) {
        this.pos = pos;
    }

    @Override
    public void run() {//由於必須要覆蓋run方法,所以這裏不能拋,只能try
        try {
            sop("W:開始將數據寫入:但等個5秒讓我們觀察...");
            Thread.sleep(5000);  //釋放cpu執行權5秒
            pos.write("W: writePiped 數據...".getBytes());  //管道輸出流
            pos.close();
        } catch (Exception e) {
            throw new RuntimeException("W:WriteThread寫入失敗...");
        }
    }

    private static void sop(Object obj) //打印
    {
        System.out.println(obj);
    }
}

//output
//R:讀取前沒有數據,阻塞中...等待數據傳過來再輸出到控制檯...
//W:開始將數據寫入:但等個5秒讓我們觀察...
//R:讀取數據成功,阻塞解除...
//W: writePiped 數據...

Reader和Writer

InputStreamReader可以把InputStream抓換成Reader;InputStreamWriter可以把InputStream轉換成Writer


自我獨立的類:RandomccessFile

RandomAccessFile是用來訪問那些保存數據記錄的文件的,你就可以用seek( )方法來訪問記錄,並進行讀寫了。這些記錄的大小不必相同;但是其大小和位置必須是可知的。但是該類僅限於操作文件。

RandomAccessFile不屬於InputStream和OutputStream類系的。實際上,除了實現DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也實現了這兩個接口),它和這兩個類系毫不相干,甚至不使用InputStream和OutputStream類中已經存在的任何功能;它是一個完全獨立的類,所有方法(絕大多數都只屬於它自己)都是從零開始寫的。這可能是因爲RandomAccessFile能在文件裏面前後移動,所以它的行爲與其它的I/O類有些根本性的不同。總而言之,它是一個直接繼承Object的,獨立的類。

基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream結合起來,再加上它自己的一些方法,比如定位用的getFilePointer( ),在文件裏移動用的seek( ),以及判斷文件大小的length( )、skipBytes()跳過多少字節數。此外,它的構造函數還要一個表示以只讀方式(“r”),還是以讀寫方式(“rw”)打開文件的參數 (和C的fopen( )一模一樣)。它不支持只寫文件。

只有RandomAccessFile纔有seek搜尋方法,而這個方法也只適用於文件。BufferedInputStream有一個mark( )方法,你可以用它來設定標記(把結果保存在一個內部變量裏),然後再調用reset( )返回這個位置,但是它的功能太弱了,而且也不怎麼實用。

RandomAccessFile的絕大多數功能,但不是全部,已經被JDK 1.4的nio的”內存映射文件(memory-mapped files)”給取代了,你該考慮一下是不是用”內存映射文件”來代替RandomAccessFile了。


public static void main(String[] args) throws IOException {  
        RandomAccessFile rf = new RandomAccessFile("rtest.dat", "rw");  
        for (int i = 0; i < 10; i++) {  
            //寫入基本類型double數據  
            rf.writeDouble(i * 1.414);  
        }  
        rf.close();  
        rf = new RandomAccessFile("rtest.dat", "rw");  
        //直接將文件指針移到第5個double數據後面  
        rf.seek(5 * 8);  //尋址
        //覆蓋第6個double數據  
        rf.writeDouble(47.0001);  
        rf.close();  
        rf = new RandomAccessFile("rtest.dat", "r");  
        for (int i = 0; i < 10; i++) {  
            System.out.println("Value " + i + ": " + rf.readDouble());  
        }  
        rf.close();  
    }  

RandomAccessFile類的應用

public class RandomAccessFileDemo {  
 public static void main(String[] args) throws Exception {  
  RandomAccessFile file = new RandomAccessFile("file", "rw");  
  // 以下向file文件中寫數據  
  file.writeInt(20);// 佔4個字節  
  file.writeDouble(8.236598);// 佔8個字節  
  file.writeUTF("這是一個UTF字符串");// 這個長度寫在當前文件指針的前兩個字節處,可用readShort()讀取  
  file.writeBoolean(true);// 佔1個字節  
  file.writeShort(395);// 佔2個字節  
  file.writeLong(2325451l);// 佔8個字節  
  file.writeUTF("又是一個UTF字符串");  
  file.writeFloat(35.5f);// 佔4個字節  
  file.writeChar('a');// 佔2個字節  

  file.seek(0);// 把文件指針位置設置到文件起始處  

  // 以下從file文件中讀數據,要注意文件指針的位置  
  System.out.println("——————從file文件指定位置讀數據——————");  
  System.out.println(file.readInt());  
  System.out.println(file.readDouble());  
  System.out.println(file.readUTF());  

  file.skipBytes(3);// 將文件指針跳過3個字節,本例中即跳過了一個boolean值和short值。  
  System.out.println(file.readLong());  

  file.skipBytes(file.readShort()); // 跳過文件中“又是一個UTF字符串”所佔字節,注意readShort()方法會移動文件指針,所以不用加2。  
  System.out.println(file.readFloat());  

  //以下演示文件複製操作  
  System.out.println("——————文件複製(從file到fileCopy)——————");  
  file.seek(0);  
  RandomAccessFile fileCopy=new RandomAccessFile("fileCopy","rw");  
  int len=(int)file.length();//取得文件長度(字節數)  
  byte[] b=new byte[len];  
  file.readFully(b);  
  fileCopy.write(b);  
  System.out.println("複製完成!");  
 }  
}  

利用RandomAccessFile實現文件的多線程下載

即多線程下載一個文件時,將文件分成幾塊,每塊用不同的線程進行下載。下面是一個利用多線程在寫文件時的例子,其中預先分配文件所需要的空間,然後在所分配的空間中進行分塊,然後寫入:

public static void main(String[] args) throws Exception {  
        // 預分配文件所佔的磁盤空間,磁盤中會創建一個指定大小的文件  
        RandomAccessFile raf = new RandomAccessFile("D://abc.txt", "rw");  
        raf.setLength(1024*1024); // 預分配 1M 的文件空間  
        raf.close();  

        // 所要寫入的文件內容  
        String s1 = "第一個字符串";  
        String s2 = "第二個字符串";  
        String s3 = "第三個字符串";  
        String s4 = "第四個字符串";  
        String s5 = "第五個字符串";  

        // 利用多線程同時寫入一個文件  
        new FileWriteThread(1024*1,s1.getBytes()).start(); // 從文件的1024字節之後開始寫入數據  
        new FileWriteThread(1024*2,s2.getBytes()).start(); // 從文件的2048字節之後開始寫入數據  
        new FileWriteThread(1024*3,s3.getBytes()).start(); // 從文件的3072字節之後開始寫入數據  
        new FileWriteThread(1024*4,s4.getBytes()).start(); // 從文件的4096字節之後開始寫入數據  
        new FileWriteThread(1024*5,s5.getBytes()).start(); // 從文件的5120字節之後開始寫入數據  
    }  

    // 利用線程在文件的指定位置寫入指定數據  
    static class FileWriteThread extends Thread{  
        private int skip;  
        private byte[] content;  

        public FileWriteThread(int skip,byte[] content){  
            this.skip = skip;  
            this.content = content;  
        }  

        public void run(){  
            RandomAccessFile raf = null;  
            try {  
                raf = new RandomAccessFile("D://abc.txt", "rw");  
                raf.seek(skip);  
                raf.write(content);  
            } catch (FileNotFoundException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            } finally {  
                try {  
                    raf.close();  
                } catch (Exception e) {  
                }  
            }  
        }  
    }  

I/O流對 典型使用方式

儘管可以通過不同的方式組合I/O流淚,但可能也就只用到其中的幾種組合。下面的例子可以作爲典型的I/O流用法的基本參考,這些示例中異常處理簡化爲簡單的拋出。

緩衝輸入文件

如果想要打開一個文件用於字符輸入,可以使用以String或File對象作爲文件名的FileInputReader,爲了提高速度,對那個文件進行緩衝,可將產生的引用傳給一個BufferedReader構造器。當readLine()返回null時,就達到了文件的末尾。

public class BufferedInputFile {  

    public static String read(String filename) throws IOException {  
        BufferedReader in = new BufferedReader(new FileReader(filename));  
        String s;  
        StringBuilder sb = new StringBuilder();  
        while ((s = in.readLine()) != null) {  
            sb.append(s+"\n");  
        }  
        in.close();  
        return sb.toString();  
    }  

    public static void main(String[] args) throws IOException {  
        System.out.print(read("BufferedInputFile.java"));  
    }  
}  
//字符串sb用來累積文件的全部內容(包括必須添加的換行符,因爲readLine()已將它們刪除),最後調用close()關閉文件。

從內存輸入

public static void main(String[] args) throws IOException {  
        StringReader in = new StringReader(read("MemoryInput.java"));  
        int c;  
        while ((c = in.read()) != -1)  
            System.out.print((char) c);  
    }  

    public static String read(String filename) throws IOException {  
        BufferedReader in = new BufferedReader(new FileReader(filename));  
        String s;  
        StringBuilder sb = new StringBuilder();  
        while ((s = in.readLine()) != null) {  
            sb.append(s + "\n");  
        }  
        in.close();  
        return sb.toString();  
    }  
    //read()是以int形式返回一下字節,因此必須類型轉換爲char才能正確打印

格式化的內存輸入

要讀取格式化數據,可以使用DataInputStream,它是一個面向字節的I/O類,因此必須使用InputStream類而不是Reader類。當然,可以用InputStream以字節形式讀取任何數據(例如一個文件),不過這裏使用的是字符串。

public static String read(String filename) throws IOException {
        BufferedReader in = new BufferedReader(new FileReader(filename));
        String s;
        StringBuilder sb = new StringBuilder();
        while ((s = in.readLine()) != null) {
            sb.append(s + "\n");
        }
        in.close();
        return sb.toString();
    }

    public static void main(String[] args) throws IOException {
        try {
            DataInputStream in = new DataInputStream(new ByteArrayInputStream(read("writertest.txt").getBytes()));
            while (true) {
                System.out.print((char) in.readByte());
            }
        } catch (EOFException e) {
            System.err.println("End of stream");
        }
    }

如果從DataInputStream用readByte()一次一個字節地讀取字符,那麼任何字節的值都是合法的結果,因此返回值不能用來檢測輸入是否結束。相反可以使用available()方法查看還有多少個可供存取的字符。下面的例子演示了怎樣一次一個字節地讀取文件:

public static String read(String filename) throws IOException {
        BufferedReader in = new BufferedReader(new FileReader(filename));
        String s;
        StringBuilder sb = new StringBuilder();
        while ((s = in.readLine()) != null) {
            sb.append(s + "\n");
        }
        in.close();
        return sb.toString();
    }

    public static void main(String[] args) throws IOException {
        try {
            DataInputStream in = new DataInputStream(new ByteArrayInputStream(read("writertest.txt").getBytes()));
            while (in.available() != 0) {//這裏判斷是重要的地方
                System.out.print((char) in.readByte());
            }
        } catch (EOFException e) {
            System.err.println("End of stream");
        }
    }
  • 注意,available()的工作方式會隨着所讀取的媒介的類型的不同而有所不同;字面意思就是“在沒有阻塞的情況下所能讀取的字節數”。對應文件,這意味着整個文件,但是對於不同類型的流,可能就不是這樣的,因此要謹慎使用。

基本的文件輸出

FileWriter對象可以向文件寫入數據。首先,創建一個與指定文件連接的FileWriter,實際上通常會用BufferedWriter將其包裝起來用以緩衝輸出,本例中爲了提供格式化機制,它被裝飾成PrintWriter。按照這種方式創建的數據文件可作爲普通文本文件讀取。

public static String read(String filename) throws IOException {  
        BufferedReader in = new BufferedReader(new FileReader(filename));  
        String s;  
        StringBuilder sb = new StringBuilder();  
        while ((s = in.readLine()) != null) {  
            sb.append(s + "\n");  
        }  
        in.close();  
        return sb.toString();  
    }  
    static String file = "BasicFileOutput.out";  
    public static void main(String[] args) throws IOException {  
        BufferedReader in = new BufferedReader(new StringReader(read("BasicFileOutput.java")));  
        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));  
        int lineCount = 1;  
        String s;  
        while ((s = in.readLine()) != null) {  
            out.println(lineCount++ + ": "+s);  
        }  
        out.close();  
        System.out.println(read(file));  
    }  

Java SE5在PrintWriter中添加了一個輔助構造器,使得不必每次希望創建文本文件並向其中寫入時,都去執行所有的裝飾工作,如下例所示:

public static String read(String filename) throws IOException {  
        BufferedReader in = new BufferedReader(new FileReader(filename));  
        String s;  
        StringBuilder sb = new StringBuilder();  
        while ((s = in.readLine()) != null) {  
            sb.append(s + "\n");  
        }  
        in.close();  
        return sb.toString();  
    }  

    static String file = "FileOutputShortcut.out";  

    public static void main(String[] args) throws IOException {  
        BufferedReader in = new BufferedReader(new StringReader(read("FileOutputShortcut.java")));  
        // Here's the shortcut:  
        PrintWriter out = new PrintWriter(file);  
        int lineCount = 1;  
        String s;  
        while ((s = in.readLine()) != null)  
            out.println(lineCount++ + ": " + s);  
        out.close();  
        // Show the stored file:  
        System.out.println(BufferedInputFile.read(file));  
    }  

存儲和恢復數據

PrintWriter可以對數據進行格式化,以便人們閱讀。但是爲了輸出可供另一個“流”恢復的數據,需要用DataOutputStream寫入數據,並用DataInputStream恢復數據。注意DataOutputStream和DataInputStream是面向字節的,因此要使用InputStream和OutputStream。

public static void main(String[] args) throws IOException {  
        DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("Data.txt")));  
        out.writeDouble(3.1415926);  
        out.writeUTF("That was pi");  
        out.writeDouble(1.41413);  
        out.writeUTF("Square root of 2");  
        out.close();  
        DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("Data.txt")));  
        System.out.println(in.readDouble());  
        System.out.println(in.readUTF());  
        System.out.println(in.readDouble());  
        System.out.println(in.readUTF());  
    }  

文件讀寫實用工具

public class TextFile extends ArrayList<String> {  

    /** 
     * Read a file as a single string   
     * @param fileName 
     * @return 
     * @throws IOException  
     */  
    public static String read(String fileName) throws IOException {  
        StringBuilder sb = new StringBuilder();  
        BufferedReader in = new BufferedReader(new FileReader(new File(fileName).getAbsoluteFile()));  
        try {  
            String s;  
            while ((s = in.readLine()) != null) {  
                sb.append(s);  
                sb.append("\n");  
            }  
        } finally {  
            in.close();  
        }  
        return sb.toString();  
    }  
    /** 
     * Write a single file  
     * @param fileName 
     * @param text 
     * @throws IOException 
     */  
    public static void write(String fileName,String text) throws IOException {  
        PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());  
        try{  
            out.print(text);  
        } finally{  
            out.close();  
        }  
    }  
    /** 
     * Read a file ,split by any regular expression 
     * @param fileName 
     * @param splitter 
     * @throws IOException 
     */  
    public TextFile(String fileName,String splitter) throws IOException{  
        super(Arrays.asList(read(fileName).split(splitter)));  
        if (get(0).equals("")) {  
            remove(0);  
        }  
    }  

    public TextFile(String fileName) throws IOException{  
        this(fileName, "\n");  
    }  

    public void write(String fileName) throws IOException {  
        PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());  
        try{  
            for (String item:this) {  
                out.println(item);  
            }  
        } finally{  
            out.close();  
        }  
    }  

    /** 
     *  
     * @param args 
     * @throws IOException  
     */  
    public static void main(String[] args) throws IOException {  
        String fileName = "TextFile.java";  
        String file = read(fileName);  
        write("test.txt", file);  
        TextFile text = new TextFile("test.txt");  
        text.write("test2.txt");  
        TreeSet<String> words = new TreeSet<String>(new TextFile(fileName, "\\W+"));  
        System.out.println(words.headSet("a"));  

        //下面代碼可以統計文檔裏各個字母出現的次數  
        TextFile text2 = new TextFile(fileName,"\\W+");  
        Map<Character, Integer> charsStat = new HashMap<Character, Integer>();  
        for (String word:text2) {  
            for (int i = 0; i < word.length(); i++) {  
                Character ch = word.charAt(i);  
                Integer freq = charsStat.get(ch);  
                charsStat.put(ch, freq == null ? 1:freq+1);  
            }  
        }  
        List<Character> keys = Arrays.asList(charsStat.keySet().toArray(new Character[0]));  
        Collections.sort(keys);  
        for (Character key:keys) {  
            System.out.println(key +" =>"+charsStat.get(key));  
        }  
    }  

}  

讀取二進制文件

public class BinaryFile {  
  public static byte[] read(File bFile) throws IOException{  
    BufferedInputStream bf = new BufferedInputStream(  
      new FileInputStream(bFile));  
    try {  
      byte[] data = new byte[bf.available()];  
      bf.read(data);  
      return data;  
    } finally {  
      bf.close();  
    }  
  }  
  public static byte[]  
  read(String bFile) throws IOException {  
    return read(new File(bFile).getAbsoluteFile());  
  }  
}  

標準I/O

在Java的類庫java.lang中我們經常用到System類的System.in,,System.out這種控制檯輸入輸出,這就是所謂的標準流。

標準I/0的使用

System.in的使用

如果單純的直接對System.in操作來接受控制檯的輸入,我們能做到的太少,因此我們要接受控制檯的輸入我們需要對它進行包裝,其中一種包裝方式就是用其他的方式進行包裝

        BufferedReader buf=new BufferedReader(new InputStreamReader(System.in));  
        String s=buf.readLine();  
        System.out.println(s);  

        //或者使用Scanner
        Scanner in=new Scanner(System.in);  
        int tt=in.nextInt();  
        System.out.println(tt); 

System.out和System.err的使用

控制檯輸出字體顏色格式不一樣


重定向

標準I/O默認的是控制檯輸入輸出,那麼我們可以將他們修改,這就是重定向,對於重定向System有3種方法,分別是setIn(InputStream),setOut(PintStream),setErr(PrintStream)。我們可以將他們重定向到文件裏,這就實現了我們常見的日誌系統

        InputStream in = new BufferedInputStream(new FileInputStream(new File(    
                "c:/in.txt")));    
        System.setIn(in);    
        PrintStream out = new PrintStream(new FileOutputStream(new File(    
                "c:/out.log")));    
        System.setOut(out);    
        PrintStream err = new PrintStream(new FileOutputStream(new File(    
                "c:/err.log")));    
        System.setErr(err);    

進程控制

你經常會需要在java內部執行其它操作系統程序,並且要控制這此程序的輸入和輸出,java類庫提供了執行這些操作的類 , 爲了捕獲程序執行時產生的標準輸出流,你需要調用getInputStream(),這是因爲。是我們可以從中讀取信息的流。

            BufferedReader br=new BufferedReader(  
                    new InputStreamReader(  
                            process.getInputStream()  //捕獲輸入流
                            ));  
            String s;  
            while((s=br.readLine())!=null)  
                System.out.println(s);  

            //捕獲錯誤流  
            BufferedReader b2=new BufferedReader(  
                    new InputStreamReader(  
                            process.getErrorStream()  
                            ));  
            while((s=b2.readLine())!=null)  
            {  
                System.err.println("sssssssss:"+s);  
            }  

參考:
1. java 編程思想第四版
2. http://blog.csdn.net/akon_vm/article/details/7429245
3. http://blog.csdn.net/zhoupenglei/article/details/46312491

發佈了33 篇原創文章 · 獲贊 10 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章