I/O筆記

目錄

1. File類

1.1 File類的常用方法

1.2 文件過濾器FileFilter

1.3 遞歸遍歷文件夾練習

 2. 字節流

2.1 基本操作

2.2 字節流讀寫示例

2.3 使用字節流進行文件複製

2.4 文件續寫和換行

3. 字符流

3.1 基本方法

3.2 字符流實現文本文件複製

4. 轉換流

4.1 概述

4.2 總結

5. 緩衝流

5.1 字節緩衝流

5.2 字符緩衝流

5.3 效率總結


 

 

1. File類

File類是一種對文件或路徑的封裝,提供了很多操作方法。

File類的構造器兩種:可以選擇將路徑寫死或者分開寫,分開寫更靈活

注:在eclipse中,通過相對路徑創建File對象,絕對路徑自動設置爲當前項目的根目錄

1.1 File類的常用方法

創建文件夾:mkdirs() 可以創建多層文件夾;mkdir()只能創建一層  

創建文件:createNewFile()

刪除:delete() 文件和文件夾都能刪,刪除的文件不走回收站,永久性刪除

獲取:

getName()獲取路徑中最後部分表示的文件或文件夾名

getPath()獲取File對象的路徑字符串,類似file.toString()

length()返回路徑中的文件的字節數,返回long型

getAbsolutePath()獲取路徑表示文件的絕對路徑(返回String)

getAbsoluteFile()獲取路徑表示文件的絕對路徑(返回File)

getParent() 獲取文件/文件夾的父路徑

getParentFile() 獲取文件/文件夾的父路徑對應的File對象

判斷:

exist()判斷File構造方法中封裝的路徑是否存在

isDirectory()判斷File構造方法中封裝的路徑是不是目錄(文件夾)

isFile()判斷File構造方法中封裝的路徑是不是文件

文件/文件夾遍歷獲取:

list()返回String[] 獲取到File構造方法中封裝的路徑下的所有文件名和文件夾名(遍歷目錄)

listFile() 同上,返回File,可以獲取絕對路徑

1.2 文件過濾器FileFilter

可用於傳遞參數給File類的listFiles(FileFilter ff),用於路徑下File的過濾

FileFilter接口沒有實現類,需要自定義過濾器

文件過濾器例子:遍歷一個路徑下的全部.java文件並打印到控制檯

首先自定義過濾器:

package ch22;

import java.io.File;
import java.io.FileFilter;

public class MyFileFilter implements FileFilter {

    @Override
    public boolean accept(File pathname) {
        return pathname.getName().endsWith(".java");
    }

}

遍歷文件夾,包括文件夾中子文件夾下的文件,使用過濾器過濾出.java文件

package ch22;

import java.io.File;

public class FileDemo {

public static void main(String[] args) {
        
        File file = new File("d:\\test");
        System.out.println("============.java文件============");
        fileFilter(file);
    }


public static void fileFilter(File file) {
        if (!file.exists()) {
            System.out.println("目錄不存在!");
        } else {
            MyFileFilter filter = new MyFileFilter();
            if (file.isDirectory()) {
                // 如果是文件夾,打印該文件夾下的.java文件
                File[] filteredFiles = file.listFiles(filter);
                for (File result : filteredFiles) {
                    System.out.println(result.getAbsolutePath());
                }
                // 繼續掃描文件夾下的全部文件,找到文件夾進行遞歸
                File[] files = file.listFiles();
                for (File aFile : files) {
                    if (aFile.isDirectory()) {
                        fileFilter(aFile);
                    }
                }
            }
        }
    }
}

1.3 遞歸遍歷文件夾練習

遞歸遍歷文件夾下全部文件和文件夾,並按一定的格式打印出來。

package ch22;

import java.io.File;

public class FileDemo {
    public static void main(String[] args) {
        // 遞歸遍歷全目錄
        File file = new File("d:\\test");
        String[] fileNames = file.list();
        for (String fileName : fileNames) {
            System.out.print(fileName + " ");
        }
        System.out.println();
        System.out.println("========================");
        fileTraverse(file, 0);
    }
    
    
    public static void fileTraverse(File file, int depth) {
        // 定義depth變量,用於記錄當前文件夾是第幾層
        int currentDepth = depth;
        if (file.exists()) {
            for (int i = 0; i < currentDepth; i++) {
                System.out.print("--");
            }
            System.out.println(file.getName());
            File[] files = file.listFiles();
            currentDepth ++;
            for (File listedFile : files) {
                if (listedFile.isFile()) {
                    for (int i = 0; i < currentDepth; i++) {
                        System.out.print("--");
                    }
                    System.out.println(listedFile.getName());
                } else if (listedFile.isDirectory()) {
                    fileTraverse(listedFile, currentDepth);
            }
            }
        }
    }

}
a b demo.txt 
========================
test
--a
----a6.txt
----aaaa
--b
----b5.txt
----wode
--demo.txt

 2. 字節流

字節流的超類是InputStream和OutputStream,常用的實現類爲FileInputStream和FileOutputStream。字節流就像一個管道,插入文件中,就可以源源不斷地讀寫字節數據。

注:所有流對象,使用完之後必須在finally塊中關閉資源

2.1 基本操作

構造方法:可以通過傳遞File類型或String類型文件全路徑來構造字節流

FileInputStream常用方法:

int read()

Reads a byte of data from this input stream.

int read(byte[] b)

Reads up to b.length bytes of data from this input stream into an array of bytes.

read()方法返回讀取到的字節內容,如果沒有讀到,則返回-1

read(byte[] b)方法將讀取到的內容存放在數組b中,返回讀取到的字節個數,沒有數據時返回-1,返回值可用於文件讀取結束的判斷.

注:這裏字節數組byte[]起到緩衝作用,虛擬機將不再每讀一個字節都去系統內存存儲一次,而是將結果存儲在數組中,待數組存滿後一起處理,這樣提高了效率,比單個字節讀取要快很多。

FileOutputStream常用方法: 

void write(byte[] b)

Writes b.length bytes from the specified byte array to this file output stream.

void write(byte[] b, int off, int len)

Writes len bytes from the specified byte array starting at offset off to this file output stream.

void write(int b)

Writes the specified byte to this file output stream.

注:這裏讀寫都是通過單個字節或者字節數組byte[],讀取到字節後,去編碼表查詢,才能轉換爲真正的文件內容,寫的時候也是將文件內容通過編碼表轉換爲字節碼,再進行寫入。

2.2 字節流讀寫示例

1. 將字節數組寫入文件

    /**   
     * @Title: FileOutput   
     * @Description: 將byte[]字節數組中的內容輸出到文件中
     * @param: @param file      
     * @return: void      
     * @throws   
     */  
    public static void FileOutput(File file) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            byte[] b = {'a','b','c', 106, 107};
            fos.write(b);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            // 沒有繼續執行的必要了,所以拋一個運行時異常終止程序
            throw new RuntimeException("文件不存在!");
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("I/O出錯!");
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        
    }
    

2. 從文件中一個字節一個字節讀取內容

/**   
     * @Title: FileInput   
     * @Description: 讀取文件中的內容,打印到控制檯   
     * @param: @param inFile      
     * @return: void      
     * @throws   
     */  
    public static void FileInput(File file) {
        if (file.exists() && file.isFile()) {
            byte[] in = new byte[1024];
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(file);
                int len = 0;
                while ((len  = fis.read(in)) != -1) {
                    System.out.print(new String(in, 0, len));
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                // 找不到文件,沒有往下執行程序的必要,直接拋運行時異常終止程序
                throw new RuntimeException("找不到指定文件!");
            } catch (IOException e) {
                e.printStackTrace();
                // io失敗,沒有往下執行的必要
                throw new RuntimeException("I/O操作失敗!");
            } finally {
                if (fis != null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

2.3 使用字節流進行文件複製

/**   
     * @Title: copy   
     * @Description: 字節流讀取單個字節複製文件   
     * @param: @param fromFile
     * @param: @param toFile      
     * @return: void      
     * @throws   
     */  
    public static void copy(File fromFile, File toFile) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream(fromFile);
            fos = new FileOutputStream(toFile);
            int len = 0;
            while ((len = fis.read()) != -1) {
                // fromFile中讀出來的數據立刻寫入toFile
                fos.write(len);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } finally {
                    if (fos != null) {
                        try {
                            fos.close();
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
/**   
     * @Title: copyByArray   
     * @Description: 字節流讀取字節數組來複制文件   
     * @param: @param fromFile
     * @param: @param toFile      
     * @return: void      
     * @throws   
     */  
    public static void copyByArray (File fromFile, File toFile) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream(fromFile);
            fos = new FileOutputStream(toFile);
            byte[] b = new byte[1024];
            int len = 0;
            while ((len = fis.read(b)) != -1) {
                // 從0開始,寫每次讀到的字節個數len
                fos.write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("複製文件失敗!");
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    // finally裏嵌套finally,保證兩個finally關流操作都執行到
                    if (fos != null) {
                        try {
                            fos.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } 
        }
    }

注:關於字節數組開闢多大空間,過小則效率提升不明顯,過大會佔用太多系統內存資源,所以要按需求進行衡量,這裏採用了一般的1024個字節。

2.4 文件續寫和換行

new FileOutputStream(file):這樣創建對象,寫入數據,會覆蓋原有的文件

FileOutputStream(File file, boolean append) :構造方法中append值傳true,這樣寫入數據,就會在原有文件的結尾續寫

換行:在windows中,換行符爲\r\n,在Linux系統中,換行符爲\n

3. 字符流

字符流的超類是java.io.Reader和java.io.Writer,常用的子類爲FileReader和FileWriter。字符流只用於操作文本文件!!!

3.1 基本方法

read():讀取單個字符並返回

read(char[]):將數據讀取到數組中,並返回讀取的個數。

注:FileWriter寫入時,必須進行flush(),內容才能從緩衝區真正寫入文件。close()也自帶flush()功能,如果寫入數據很多,不能依靠close()的刷新功能,必須邊寫邊刷。

3.2 字符流實現文本文件複製

/**   
     * @Title: copyByChar   
     * @Description: 通過字符流複製文本文件   
     * @param: @param fromFile
     * @param: @param toFile      
     * @return: void      
     * @throws   
     */  
    public static void copyByChar(File fromFile, File toFile) {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            fr = new FileReader(fromFile);
            fw = new FileWriter(toFile);
            char[] cbuf = new char[1024];
//            fw.write("你好");
            int len = 0;
            while ((len = fr.read(cbuf)) != -1) {
                System.out.println(new String(cbuf, 0, len));
                fw.write(cbuf, 0, len);
                // 必須flush()一下
                fw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("複製文件失敗!");
        } finally {
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } finally {
                    if (fw != null) {
                        try {
                            fw.close();
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

注:如果文本文檔中包含中文字符,則可能會產生亂碼問題。

涉及到字節和字符的轉換,首先了解編碼和解碼的過程:

文字--->(數字) :編碼。 “abc”.getBytes()  獲取byte[]

(數字)--->文字  : 解碼。 byte[] b={97,98,99}  new String(b)獲取到字符串

使用字符流讀寫時,虛擬機會自動進行解碼和編碼的工作,如果編碼表不相同,則會產生亂碼問題。一般文件編碼默認是ANSI編碼,簡體中文下是GB2312。

解決辦法:

1. 通過轉換流FileInputStream轉碼。

    InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "GBK"); //或GB2312,GB18030
    BufferedReader read = new BufferedReader(isr);

 2. 如非要用FileReader的話,可以將要讀取的文件改爲通用的編碼(如UTF-8).如txt的文件可以在另存爲中設置編碼。然後讀取 顯示寫入都是正常的。

4. 轉換流

4.1 概述

轉換流包括InputStreamReader和OutputStreamWriter,這兩個類是Reader和Writer類的子類,也是FileReader和FileWriter的父類。轉換流是字符和字節之間的橋樑,通過包裝一個字節流,對文件進行操作。

OutputStreamWriter:可使用指定的字符編碼表,將要寫入流中的字符編碼成字節。它的作用的就是,將字符串按照指定的編碼表轉成字節,在使用字節流將這些字節寫出去。

OutputStreamWriter流對象,它到底如何把字符轉成字節輸出的呢?

其實在OutputStreamWriter流中維護自己的緩衝區,當我們調用OutputStreamWriter對象的write方法時,會拿着字符到指定的碼錶中進行查詢,把查到的字符編碼值轉成字節數存放到OutputStreamWriter緩衝區中。然後再調用刷新功能,或者關閉流,或者緩衝區存滿後會把緩衝區中的字節數據使用字節流寫到指定的文件中。

InputStreamReader:同理

查看api文檔可以知道,InputStreamReader和OutputStreamWriter用法基本與FileReader和FileWriter相同,只有構造方法有區別:

Constructor and Description
InputStreamReader(InputStream in)

Creates an InputStreamReader that uses the default charset.

InputStreamReader(InputStream in, Charset cs)

Creates an InputStreamReader that uses the given charset.

InputStreamReader(InputStream in, CharsetDecoder dec)

Creates an InputStreamReader that uses the given charset decoder.

InputStreamReader(InputStream in, String charsetName)

Creates an InputStreamReader that uses the named charset.

Constructor and Description
OutputStreamWriter(OutputStream out)

Creates an OutputStreamWriter that uses the default character encoding.

OutputStreamWriter(OutputStream out, Charset cs)

Creates an OutputStreamWriter that uses the given charset.

OutputStreamWriter(OutputStream out, CharsetEncoder enc)

Creates an OutputStreamWriter that uses the given charset encoder.

OutputStreamWriter(OutputStream out, String charsetName)

Creates an OutputStreamWriter that uses the named charset.

注:傳遞String類型的參數,目前只用得到兩種"GBK"和"utf-8",如果不傳遞charset參數,則默認GBK字符集。

4.2 總結

FileReader 是InputStreamReader的子類,也就是一種簡化實現,它不需要傳遞字符集,只能依據默認字符集,完成字節到字符的轉換。以下三種寫法的功能是相同的:

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默認字符集。

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。

FileReader fr = new FileReader("a.txt");

FileWriter同理。

5. 緩衝流

前面進行字節流和字符流的讀寫時,爲了提高讀寫效率,我們採用數組作爲緩衝區,其實Java本身也提供了高效率的IO流,就是緩衝流。

緩衝流分爲字節緩衝流和字符緩衝流。

5.1 字節緩衝流

BufferedInputStream(InputStream in):封裝一個基本流,以提高它的讀取效率

BufferedOutputStream(OutputStream out):封裝一個基本流,以提高它的寫入效率

其他用法與字節流相同。

例子:字節緩衝流複製文件:

/**   
     * @Title: bufferedCopy   
     * @Description: 使用緩衝字節流進行文件複製
     * @param: @param fromFile      
     * @return: void      
     * @throws   
     */  
    public static void bufferedCopy(File fromFile, File toFile) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(fromFile));
            bos = new BufferedOutputStream(new FileOutputStream(toFile));
            int len = 0;
            byte[] b = new byte[1024];
            while ((len = bis.read(b)) != -1) {
                bos.write(b, 0, len);
                bos.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (bos != null) {
                        try {
                            bos.close();
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

5.2 字符緩衝流

BufferedReader(Reader in):封裝一個基本字符流,提高它的讀取效率

BufferedWriter(Writer out):封裝一個基本字節流,提高它的寫入效率

除了幾個基本方法,BufferedReader的特有方法:

public String nextLine():返回一個文本行,不包含任何換行符,如果已經到流末尾,則返回null

相應地,BufferedWriter的特有方法:

public void newLine():寫入一個換行符,至於換行符具體是什麼,根據當前系統而定,win是\n\r,linux是\n

例子:字符緩衝流複製文件

/**   
     * @Title: bufferedCopyByChar   
     * @Description: 緩衝字符流實現文件複製
     * @param: @param fromFile
     * @param: @param toFile      
     * @return: void      
     * @throws   
     */  
    public static void bufferedCopyByChar(File fromFile, File toFile) {
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
             br = new BufferedReader(new FileReader(fromFile));
             bw = new BufferedWriter(new FileWriter(toFile));
             String line = null;
             while((line = br.readLine()) != null) {
                 // 寫入讀出的一行,不包括換行符標誌
                 bw.write(line);
                 // 寫入換行!!!
                 bw.newLine();
             }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (bw != null) {
                        try {
                            bw.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

5.3 效率總結

以字節流爲例:

基本字節流單個字節複製文件效率極低,不要使用這種方法;採用字節數組byte[]緩衝可以明顯提高效率

採用java自帶的緩衝流,單個字節複製文件效率比基本流+緩衝數組的方法稍低一些;緩衝流+字節數組的方法效率最高,推薦使用這種方法,即5.1中緩衝流的例子

任何情況下都儘量不要使用單個字節讀寫的方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章