1. 程式人生 > >Java IO流操作彙總: inputStream 和 outputStream

Java IO流操作彙總: inputStream 和 outputStream

我們在進行Android java 開發的時候,經常會遇到各種IO流操作。IO流操作一般分為兩類:字元流和位元組流。以“Reader”結尾都是字元流,操作的都是字元型的資料;以“Stream”結尾的都是位元組流,操作的都是byte資料。現將各種常見IO流總結如下:

一、位元組流

1. InputStream 和 OutputStream

InputStream 和 OutputStream為各種輸入輸出位元組流的基類,所有位元組流都繼承這兩個基類。

2. FileInputStream 和 FileOutputStream

這兩個從字面意思很容易理解,是對檔案的位元組流操作,也會最常見的IO操作流。

    /*
     * 以位元組為單位讀取檔案,常用於讀二進位制檔案,如圖片、聲音、影像等檔案。
     */
    public static void readFileByBytes(String inFile, String outFile) {
        File file = new File(fileName);
        InputStream in = null;
        OutputStream out = null;
        try {
            byte[] tempbytes = new byte[100];
            int byteread = 0;
            in = new FileInputStream(inFile);
            out = new FileOutputStream(outFile);
            while ((byteread = in.read(tempbytes)) != -1) {
                out.write(tempbytes, 0, byteread);
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e1) {
                }
                try {
                    out.close();
                } catch (IOException e1) {
                }
            }
        }
    }

3.DataInputStream和DataOutputStream

DataInputStream 是資料輸入流,它繼承於FilterInputStream。
DataOutputStream 是資料輸出流,它繼承於FilterOutputStream。
二者配合使用,“允許應用程式以與機器無關方式從底層輸入流中讀寫基本 Java 資料型別”。

    /**
     * DataOutputStream的API測試函式
     */
    private static void testDataOutputStream() {
        DataOutputStream out = null;
        try {
            File file = new File("file.txt");
            out = new DataOutputStream(new FileOutputStream(file));
            
            out.writeBoolean(true);
            out.writeByte((byte)0x41);
            out.writeChar((char)0x4243);
            out.writeShort((short)0x4445);
            out.writeInt(0x12345678);
            out.writeLong(0x0FEDCBA987654321L);
            out.writeUTF("abcdefg");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch(IOException e) {
            }
        }
    }
    
    /**
     * DataInputStream的API測試函式
     */
    private static void testDataInputStream() {
        DataInputStream in = null;
        try {
            File file = new File("file.txt");
            in = new DataInputStream(new FileInputStream(file));

            System.out.printf("byteToHexString(0x8F):0x%s\n", byteToHexString((byte)0x8F));
            System.out.printf("charToHexString(0x8FCF):0x%s\n", charToHexString((char)0x8FCF));
            System.out.printf("readBoolean():%s\n", in.readBoolean());
            System.out.printf("readByte():0x%s\n", byteToHexString(in.readByte()));
            System.out.printf("readChar():0x%s\n", charToHexString(in.readChar()));
            System.out.printf("readShort():0x%s\n", shortToHexString(in.readShort()));
            System.out.printf("readInt():0x%s\n", Integer.toHexString(in.readInt()));
            System.out.printf("readLong():0x%s\n", Long.toHexString(in.readLong()));
            System.out.printf("readUTF():%s\n", in.readUTF());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch(IOException e) {
            }
        }
    }

4.BufferedInputStream 和 BufferedOutputStream

BufferedInputStream是帶緩衝區的輸入流,它繼承於FilterInputStream。預設緩衝區大小是8M,能夠減少訪問磁碟的次數,提高檔案讀取效能。

BufferedOutputStream是帶緩衝區的輸出流,它繼承於FilterOutputStream,能夠提高檔案的寫入效率。

它們提供的“緩衝功能”本質上是通過一個內部緩衝區陣列實現的。例如,在新建某輸入流對應的BufferedInputStream後,當我們通過read()讀取輸入流的資料時,BufferedInputStream會將該輸入流的資料分批的填入到緩衝區中。每當緩衝區中的資料被讀完之後,輸入流會再次填充資料緩衝區;如此反覆,直到我們讀完輸入流資料。

    public static void readAndWrite(String[] args) {    
        try {
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("f:/a.mp3"));
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("f:/b.mp3"));
            byte[] b=new byte[1024];
            int len=0;
            while(-1!= (len = bis.read(b, 0, b.length))) {
                bos.write(b, 0, len);
            }

        } catch (FileNotFoundException e) {
            System.out.println("檔案找不到");
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if (null! = bos){
                bos.close();
            }
            if (null! = bis){
                bis.close();
            }
        }
    }

5. ByteArrayInputStream 和 ByteArrayOutputStream

該類從記憶體中的位元組陣列中讀取資料,它的資料來源是一個位元組陣列,它們分別繼承自InputStream 和 OutputStream。

ByteArrayInputStream類的構造方法包括: 
ByteArrayInputStream(byte[] buf)--------引數buf指定位元組陣列型別的資料來源。 
ByteArrayInputStream(byte[] buf, int offset, int length)-----引數buf指定位元組陣列型別資料來源,引數offset指定從陣列中開始讀取資料的起始下標位置,length指定從陣列中讀取的位元組數。 

    private static byte[] readWithByteArray(byte[] dataSource) {
        ByteArrayInputStream in = null;
        ByteArrayOutputStream out = null;
        
        try {
            in = new ByteArrayInputStream(dataSource);
            out = new ByteArrayOutputStream();
            
            int len = 0;
            byte[] buffer = new byte[1024];
            while ((len = in.read(buffer, 0, buffer.length)) != -1){
                out.write(buffer, 0, len);
            }
            return out.toByteArray();
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e1) {
            }
            try {
                out.close();
            } catch (IOException e1) {
            }
        }
    }

二、字元流

1. InputStreamReader 和 OutputStreamWriter

 InputStreamReader 和 OutputStreamWriter為各種輸入輸出字元流的基類,所有字元流都繼承這兩個基類。實際上,這兩個類內部各自持有一個inputStream 和 outputStream物件,相當於是對inputStream 和 outputStream進行了包裝,將輸入位元組流轉換成字元流,便於讀寫操作。
    /**
     * 以字元為單位讀取檔案,常用於讀文字,數字等型別的檔案
     */
    public static void readFileByChars(String fileName) {
        File file = new File(fileName);
        Reader reader = null;
        try {
            System.out.println("以字元為單位讀取檔案內容,一次讀一個位元組:");
            //1. 一次讀一個字元
            reader = new InputStreamReader(new FileInputStream(file));//可以是任意的InputStream類,不一定必須是FileInputStream
            int tempchar;
            while ((tempchar = reader.read()) != -1) {
                if (((char) tempchar) != '\r') {
                    System.out.print((char) tempchar);
                }
            }
            reader.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            System.out.println("以字元為單位讀取檔案內容,一次讀多個位元組:");
            //2. 一次讀多個字元
            char[] tempchars = new char[30];
            int charread = 0;
            reader = new InputStreamReader(new FileInputStream(fileName));
            while ((charread = reader.read(tempchars)) != -1) {
                for (int i = 0; i < charread; i++) {
                    if (tempchars[i] != '\r') {
                        System.out.print(tempchars[i]);
                    }
                }
            }
 
        } catch (Exception e1) {
            e1.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
    }
 

2. FileReader 和 FileWriter

FileReader 和 FileWriter分別繼承自 inputStreamReader 和 outputStreamWriter。它是對讀取檔案作業系統的封裝,所有的讀寫都是直接操作檔案系統。因此如果是頻繁讀寫操作,不建議使用FileReader 和 FileWriter,效能將會非常低,這時你需要使用BufferedReader

(1)FileWriter類
構造方法:
FileWriter fw = new FileWriter(String fileName);//建立字元輸出流類物件和已存在的檔案相關聯。檔案不存在的話,並建立。
FileWriter fw = new FileWriter(String fileName,boolean append);//建立字元輸出流類物件和已存在的檔案相關聯,並設定該該流對檔案的操作是否為續寫。
主要方法: 

write(char[] buffer, int offset, int count) //將字元陣列寫入,offset為陣列的起始地址,count為需要寫入的字元數
void write(String str)  //寫入字串。當執行完此方法後,字元資料還並沒有寫入到目的檔案中去。此時字元資料會儲存在緩衝區中。
viod flush() //重新整理該流中的緩衝。將緩衝區中的字元資料儲存到目的檔案中去。
viod close() //關閉此流。在關閉前會先重新整理此流的緩衝區。在關閉後,再寫入或者重新整理的話,會拋IOException異常。

(2)FileReader類
構造方法:
FileReader fr = new FileReader(String fileName);  //使用帶有指定檔案的String引數的構造方法。建立該輸入流物件。並關聯原始檔。
主要方法:
int read();   // 讀取單個字元。返回作為整數讀取的字元,如果已達到流末尾,則返回 -1。
int read(char []cbuf);  //將字元讀入陣列。返回讀取的字元數。如果已經到達尾部,則返回-1。
void close();   //關閉此流物件。釋放與之關聯的所有資源。

    public static void readAndWrite() {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fr = new FileReader("C:\\my.txt");
            fw = new FileWriter("D:\\you.txt");
            //1.讀一個字元,寫一個字元方法
            int ch = 0;  
            while ((ch = fr.read()) != -1) {  
                fw.write(ch);  
            } 
            
            //2.讀一個數組大小,寫一個數組大小方法。
            char []buf = new char[1024];
            int len = 0;
            while((len = fr.read(buf)) != -1){
                fw.write(buf, 0, len);              
            }
            
            //3.直接寫一個字串
            fw.write("hello world!");
        } catch (Exception e) {
            System.out.println(e.toString());
        } finally {
            if (fr != null)
                try {
                    fr.close();
                } catch (Exception e2) {
                    throw new RuntimeException("關閉失敗!");
                }
            if (fw != null){
                try {
                    fw.close();
                } catch (IOException e) {
                    throw new RuntimeException("關閉失敗!");
                }
            }
        }
    }

3. BufferedReader 和 BufferedWriter

(1)BufferedReader和BufferedWriter類各擁有8192字元的緩衝區。當BufferedReader在讀取文字檔案時,會先儘量從檔案中讀入字元資料並置入緩衝區,而之後若使用read()方法,會先從緩衝區中進行讀取。如果緩衝區資料不足,才會再從檔案中讀取,使用BufferedWriter時,寫入的資料並不會先輸出到目的地,而是先儲存至緩衝區中。如果緩衝區中的資料滿了,才會一次對目的地進行寫出。
(2)從標準輸入流System.in中直接讀取使用者輸入時,使用者每輸入一個字元,System.in就讀取一個字元。為了能一次讀取一行使用者的輸入,使用了BufferedReader來對使用者輸入的字元進行緩衝。readLine()方法會在讀取到使用者的換行字元時,再一次將整行字串傳入

    /**
     * 以行為單位讀取檔案,常用於讀面向行的格式化檔案
     */
    public static void readWithBufferedReader(String fileName) {
        File file = new File(fileName);
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(file));
            String tempString = null;
            int line = 1;
            // 一次讀入一行,直到讀入null為檔案結束
            while ((tempString = reader.readLine()) != null) {
                System.out.println("line " + line + ": " + tempString);
                line++;
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                }
            }
        }
    }
System.in是一個位流,為了轉換為字元流,可使用InputStreamReader為其進行字元轉換,然後再使用BufferedReader為其增加緩衝功能。例如:
    public static void readAndWrite() {
        try {
            //緩衝System.in輸入流
            //System.in是位流,可以通過InputStreamReader將其轉換為字元流
            BufferedReader bufReader = new BufferedReader(new InputStreamReader(System.in));
            //緩衝FileWriter
            BufferedWriter bufWriter = new BufferedWriter(new FileWriter("/sdcard/log/test.txt"));

            String input = null;
            //每讀一行進行一次寫入動作
            while(!(input = bufReader.readLine())) {
                bufWriter.write(input);
                //newLine()方法寫入與作業系統相依的換行字元,依執行環境當時的OS來決定該輸出那種換行字元
                bufWriter.newLine();
            }
            bufReader.close();
            bufWriter.close();
        } catch(ArrayIndexOutOfBoundsException e) {
            System.out.println("沒有指定檔案");
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

三、 RandomAccessFile 

RandomAccessFile不屬於InputStream和OutputStream類系的。實際上,它和這兩個類系毫不相干,甚至不使用InputStream和OutputStream類中已經存在的任何功能;它是一個完全獨立的類。這可能是因為RandomAccessFile能在檔案裡面前後移動,所以它的行為與其它的I/O類有些根本性的不同。
RandomAccessFile的基本功能有:定位用的getFilePointer( ),在檔案裡移動用的seek( ),以及判斷檔案大小的length( )、skipBytes()跳過多少位元組數。此外,它的建構函式還要一個表示以只讀方式("r"),還是以讀寫方式("rw")開啟檔案的引數。實際它和C的fopen()一模一樣,都是直接對檔案控制代碼操作。

    /**
     * 隨機讀取檔案內容
     */
    public static void readFileByRandomAccess(String fileName) {
        RandomAccessFile randomFile = null;
        try {
            // 開啟一個隨機訪問檔案流,按只讀方式
            randomFile = new RandomAccessFile(fileName, "rw");
            long fileLength = randomFile.length();

            // 設定讀寫檔案的起始位置
            randomFile.seek(0);

            // 一次讀取多個數據
            byte[] bytes = new byte[10];
            int byteread = 0;
            while ((byteread = randomFile.read(bytes)) != -1) {
                System.out.write(bytes, 0, byteread);
            }
            //一次寫入多個數據
            randomFile.write(bytes);
            
            // 一次讀取單個數據
            randomFile()
            // 一次寫入單個數據
            randomFile.writeDouble(47.0001);  

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (randomFile != null) {
                try {
                    randomFile.close();
                } catch (IOException e1) {
                }
            }
        }
    }