全面掌握IO(輸入/輸出流)


File類:

程序中操作文件和目錄都可以使用File類來完成即不管是文件還是目錄都是使用File類來操作的,File能新建,刪除,重命名文件和目錄,但File不能訪問文件內容本身,如果需要訪問文件本身,則需要使用輸入/輸出流,該類是位於java.io包下的

輸入與輸出IO:

輸入流:只能從中讀取數據,而不能向其中寫入數據(由InputStream(字節流)和Reader(字符流)作為基類)

輸出流:只能向其寫入數據,而不能從中讀取數據(由OutputStream(字節流)和Writer(字符流)作為基類)

java的io總共涉及40多個類,但都是從這四個抽象基類中派生出來的

 

InputStream最重要的三個read方法:

Reader中的read方法:

從這兩個抽象類提供的方法就可以看出其實功能基本是一樣的,只是操作的數據單元不一樣而已

由於InputStream與Reader都是抽象類,是不能進行實例化的,我們只能用他們的子類來創建實例,它們分別提供了一個子類用於讀取文件的輸入流:FileInputStream和

FileReader,這兩個子類都是節點流(與處理流相對)-----會直接與指定的文件關聯而無包裝。下面代碼演示FileInputStream使用read(byte[] b):

package xidian.sl.io;

import java.io.FileInputStream;
import java.io.FileReader;

public class InputStreamTest {
    /**
     * 使用FileInputStream讀取該類本身
     * */
    public static void FileInputStreamTest() throws Exception{
        FileInputStream fis = null;
        try{
        //創建字節輸入流
        fis = new FileInputStream("src/xidian/sl/io/InputStreamTest.java");
        //創建一個長度為1024的字節數組來存取
        byte[] bbuf = new byte[1024];
        //用於保存實際讀取的字節數
        int hasRead = 0;
        //使用循環來進行重復讀取
        while((hasRead = fis.read(bbuf))> 0){
            //取出字節,將字節數組轉化為字符串輸出
            System.out.println(new String(bbuf, 0 , hasRead));
        }
        }finally{
            //關閉文件輸入流
            fis.close();
        }
    }
    /**
     * 使用FileReader讀取該類本身
     * */
    public static void FileReaderTest() throws Exception{    
        FileReader fr = null;
        try{
        //創建字節輸入流
        fr = new FileReader("src/xidian/sl/io/InputStreamTest.java");
        //創建一個長度為1024的字節數組來存取
        char[] bbuf = new char[40];
        //用於保存實際讀取的字節數
        int hasRead = 0;
        //使用循環來進行重復讀取
        while((hasRead = fr.read(bbuf))> 0){
            //取出字節,將字節數組轉化為字符串輸出
            System.out.println(new String(bbuf, 0 , hasRead));
        }
        }finally{
            //關閉文件輸入流
            fr.close();
        }
    }
    public static void main(String[] args) throws Exception{
        InputStreamTest.FileInputStreamTest();
        InputStreamTest.FileReaderTest();
    }
}

可以看到這兩個子類的使用方式可以說是完全一樣的,不過這里要注意一個問題:字節流FileInputStream是根據字節來讀取的,而一個中文是占兩個字節的,如果包含很多中文的文件被字節流分多次進行讀取,可能會造成亂碼,因為有可能會導致剛好將一個中文分兩次讀取,這樣就會亂碼了,因此如果中文包含多的話還是使用字符流FileReader比較好

在字節流與字符流之間選擇的規律:如果需要進行輸入/輸出的內容是文本內容,則應該考慮使用字符流,如果需要進行輸入/輸出的是二進制內容,則應該考慮使用字節流,因為字節流的功能比字符流強大,計算機中所有的數據都是二進制的,而字節流可以處理所有的二進制文件;

 

OutputStream中最重要的write方法:

Writer中最重要的write方法:

Writer類中多了兩個對字符串的操作類,因此如果是直接輸出字符串就選用Writer會比較的方便;

與輸入流一樣,輸出流也有兩個文件操作的子類:FileOutputStream和FileWrite

package xidian.sl.io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;

public class OutputStreamTest {
    /**
     * 使用字節流輸出
     * */
    public static void FileOutputStreamTest() throws Exception{
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try{
            //創建字節輸入流
            fis = new FileInputStream("src/xidian/sl/io/InputStreamTest.java");
            //創建字節輸出流
            fos = new FileOutputStream("src/xidian/sl/io/Output.txt");
            byte[] bt = new byte[40];
            int hasRead = 0;
            //循環從輸入流中讀取數據
            while((hasRead = fis.read(bt))> 0){
                //每讀取一個,即寫入文件輸出流,讀了多少就寫多少
                fos.write(bt, 0, hasRead);
            }
            
        }catch (Exception e) {
            e.printStackTrace();
        }finally{
            /**
             * 流在關閉時會自動執行flash,將緩沖中的數據flush到物理節點中
             * 所以關閉時很重要的
             * */
            if(fis != null){
                fis.close();
            }
            if(fos != null){
                fos.close();
            }
        }
    }
    /**
     * 使用字符流輸出字符串會顯得比較的方便
     * */
    public static void FileWriteTest() throws Exception{
        FileWriter fw = null;
        try{
            //創建字節輸出流
            fw = new FileWriter("src/xidian/sl/io/Output.txt");
            fw.write("溫州醫學院\r\n");
            fw.write("信息與管理專業\r\n");
            fw.write("溫州醫學院\r\n");
            fw.write("溫州醫學院\n");
            fw.write("溫州醫學院");
        }catch (Exception e) {
            e.printStackTrace();
        }finally{
            if(fw != null){
                fw.close();
            }
        }
    }
    public static void main(String[] args) throws Exception{
        OutputStreamTest.FileOutputStreamTest();
        OutputStreamTest.FileWriteTest();
    }
}


 

上面是節點流的基本使用,下面將了解處理流的使用,處理流會顯得更加的高效

區分節點流於處理流的方法是:只要流的構造器的參數不是一個物理節點,而是已存在的流,那這個流一定是處理流,因為所有的節點流都是直接以物理io節點作為構造器的參數、

(如file)。

舉例:PrintStream處理流來封裝FileOutputStream節點流,進行輸出,由於PrintStream類的輸出功能非常的強大,因此我們需要輸出文本內容一般都會將輸出流包裝成PrintStream后輸出

package xidian.sl.io;

import java.io.FileOutputStream;
import java.io.PrintStream;

public class PrintStreamTest {
    public static void main(String[] args){
        PrintStream ps = null;
        try{
            //創建一個節點輸出流
            FileOutputStream fos = new FileOutputStream("src/xidian/sl/io/Output.txt");
            //以PrintStream處理流來包裝FileOutputStream節點流
            ps = new PrintStream(fos);
            ps.println("普通字符串");
            ps.println(new PrintStreamTest());
        }catch (Exception e) {
            e.printStackTrace();
        }finally{
            ps.close();
        }
    }
}

其實我們一直使用的標准輸出System.out的類型都是PrintStream:

從上面的實例就可以看出將節點流封裝成處理流很簡單,只需調用處理流的構造方法來傳入節點流就可以了;而且看到上面流的關閉只是關閉了處理流而未去關閉節點流,這樣做是完全正確的,以后我們在關閉流的時候只需要關閉最上層的處理流即可

 

字符流中有兩個特別的流來處理字符串的:StringReader和StringWriter,

可以看到使用時實例化只需要傳入一個字符串即可:例子:

package xidian.sl.io;

import java.io.StringReader;
import java.io.StringWriter;

public class StringNodeTest {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String src = "你是個神";
        StringReader sr = new StringReader(src);
        char[] chars = new char[40];
        int hasRead = 0;
        try{
            //采用循環的方式
            while((hasRead = sr.read(chars))>0){
                System.out.println(new String(chars, 0, hasRead));
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally{
            sr.close();
        }
        //創建StringWriter
        StringWriter sw = new StringWriter(40);
        sw.write("你是一個大神");
        sw.write("你也是一個大神");
        System.out.println(sw.toString());
        
    }

}


io系統提供的兩個轉換流:InputStreamReader和OutputStreamWriter,都是將字節流轉化為字符流

在java中是使用System.in來提供鍵盤輸入的,但這個標准輸入流是InputStream類的實例:

而前面講到了當處理的是文本內容時,使用字符流會顯得比較方便,正好鍵盤輸入就是文本的操作,因此我們有必須將System.in轉換為字符流:

package xidian.sl.io;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class KeyinTest {
    public static void main(String[] args){
        BufferedReader br = null;
        try{
            //將System.in對象轉化為Reader對象
            InputStreamReader isr = new InputStreamReader(System.in);
            //將節點流包裝為處理流
            br = new BufferedReader(isr);
            String buffer = null;
            //采用循環的方式一行一行讀取
            while((buffer = br.readLine()) != null){
                System.out.print("輸入的內容 = "+ buffer);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

PrintStream是有強大的輸出功能,而BufferReader有強大的輸入(即讀取),因此在操作讀取文本內容時盡可能將其轉化為BufferReader,可以方便的使用readLine()方法

 

 接下來最為強大的文件操作類RandomAccessFile來了,這個類既可以向文件輸入數據,也可以輸出數據,並且他與不同的流最大的不同就是“支持文件任意位置的訪問”,即程序可以控制讀取文件哪個位置的內容;

從構造方法上可以看出,除了提供一個文件或文件名外還需要提供一個String參數mode,mode規定了RandomAccessFile類訪問文件的模式:

1. “r”:以只讀的方式打開指定文件

2. “rw”:以讀取,寫入方式打開指定文件,並且文件不存在會自動進行創建

3.“rws”與“rwd”:與“rw”類似,只是要求文件內容或元數據的每個更新都同步寫入底層存儲設備

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM