計算機程序的思惟邏輯 (59) - 文件和目錄操做

本系列文章經補充和完善,已修訂整理成書《Java編程的邏輯》(馬俊昌著),由機械工業出版社華章分社出版,於2018年1月上市熱銷,讀者好評如潮!各大網店和書店有售,歡迎購買:京東自營連接 html

前面兩節咱們介紹瞭如何經過流的方式讀寫文件內容,本節咱們介紹文件元數據和目錄的一些操做。java

文件和目錄操做最終是與操做系統和文件系統相關的,不一樣系統的實現是不同的,但Java中的java.io.File類提供了統一的接口,底層它會經過本地方法調用操做系統和文件系統的具體實現,本節,咱們就來介紹File類。編程

File類中的操做大概能夠分爲三類:數組

  • 文件元數據
  • 文件操做
  • 目錄操做

在介紹這些操做以前,咱們先來看下File的構造方法。安全

構造方法

File既能夠表示文件,也能夠表示目錄,它的主要構造方法有:bash

public File(String pathname) public File(String parent, String child) public File(File parent, String child) 複製代碼

能夠是一個參數pathname,表示完整路徑,該路徑能夠是相對路徑,也能夠是絕對路徑。還能夠是兩個參數,表示父目錄的parent和表示孩子的child。微信

File中的路徑能夠是已經存在的,也能夠是不存在的。ide

經過new新建一個File對象,不會實際建立一個文件,只是建立一個表示文件或目錄的對象,new以後,File對象中的路徑是不可變的。ui

文件元數據

文件名與文件路徑

有了File對象後,就能夠獲取它的文件名和路徑信息,相關方法有:spa

public String getName() public boolean isAbsolute() public String getPath() public String getAbsolutePath() public String getCanonicalPath() throws IOException public String getParent() public File getParentFile() public File getAbsoluteFile() public File getCanonicalFile() throws IOException 複製代碼

getName()返回的就是文件或目錄名稱,不含路徑名。isAbsolute()判斷File中的路徑是不是絕對路徑。

getPath()返回構造File對象時的完整路徑名,包括路徑和文件名稱。getAbsolutePath()返回完整的絕對路徑名。getCanonicalPath()返回標準的完整路徑名,它會去掉路徑中的冗餘名稱如".","..",跟蹤軟鏈接(Unix系統概念)等。這三個路徑容易混淆,咱們看一個例子來講明:

File f = new File("../io/test/students.txt");
System.out.println(System.getProperty("user.dir"));
System.out.println("path: " + f.getPath());
System.out.println("absolutePath: " + f.getAbsolutePath());
System.out.println("canonicalPath: " + f.getCanonicalPath());
複製代碼

這裏,使用相對路徑來構造File對象,..表示上一級目錄,輸出爲:

/Users/majunchang/io
path: ../io/test/students.txt
absolutePath: /Users/majunchang/io/../io/test/students.txt
canonicalPath: /Users/majunchang/io/test/students.txt
複製代碼

當前目錄爲/Users/majunchang/io,getPath()返回的就是構造File對象時使用的相對路徑,而getAbsolutePath()返回的是完整路徑,可是包含冗餘路徑"../io/",而getCanonicalPath()則去除了該冗餘路徑。

getParent()返回父目錄路徑,getParentFile()返回父目錄的File對象,須要注意的是,若是File對象是相對路徑,則這些方法可能得不到父目錄,好比:

File f = new File("students.txt");
System.out.println(System.getProperty("user.dir"));
System.out.println("parent: " + f.getParent());
System.out.println("parentFile: " + f.getParentFile());
複製代碼

輸出爲:

/Users/majunchang/io
parent: null
parentFile: null
複製代碼

即便是有父目錄的,getParent()的返回值也是null。那如何解決這個問題呢?能夠先使用getAbsoluteFile()或getCanonicalFile()方法,它們都返回一個新的File對象,新的File對象分別使用getAbsolutePath()和getCanonicalPath()的返回值做爲參數構造。好比,修改上面的代碼爲:

File f = new File("students.txt");
System.out.println(System.getProperty("user.dir"));
System.out.println("parent: " + f.getCanonicalFile().getParent());
System.out.println("parentFile: " + f.getCanonicalFile().getParentFile());
複製代碼

此次,就能獲得父目錄了,輸出爲:

/Users/majunchang/io
parent: /Users/majunchang/io
parentFile: /Users/majunchang/io
複製代碼

File類中有四個靜態變量,表示路徑分隔符,它們是:

public static final String separator
public static final char separatorChar
public static final String pathSeparator
public static final char pathSeparatorChar
複製代碼

separator和separatorChar表示文件路徑分隔符,在Windows系統中,通常爲"",Linux系統中通常爲"/"。

pathSeparator和pathSeparatorChar表示多個文件路徑中的分隔符,好比環境變量PATH中的分隔符,Java類路徑變量classpath中的分隔符,在執行命令時,操做系統會從PATH指定的目錄中尋找命令,Java運行時加載class文件時,會從classpath指定的路徑中尋找類文件。在Windows系統中,這個分隔符通常爲';',在Linux系統中,這個分隔符通常爲':'。

文件基本信息

除了文件名和路徑,File對象還有以下方法,以獲取文件或目錄的基本信息:

//文件或目錄是否存在
public boolean exists() //是否爲目錄 public boolean isDirectory() //是否爲文件 public boolean isFile() //文件長度,字節數 public long length() //最後修改時間,從紀元時開始的毫秒數 public long lastModified() //設置最後修改時間,設置成功返回true,不然返回false public boolean setLastModified(long time) 複製代碼

對於目錄,length()方法的返回值是沒有意義的。

須要說明的是,File對象沒有返回建立時間的方法,由於建立時間不是一個公共概念,Linux/Unix就沒有建立時間的概念。

安全和權限信息

File類中與安全和權限相關的方法有:

//是否爲隱藏文件
public boolean isHidden() //是否可執行 public boolean canExecute() //是否可讀 public boolean canRead() //是否可寫 public boolean canWrite() //設置文件爲只讀文件 public boolean setReadOnly() //修改文件讀權限 public boolean setReadable(boolean readable, boolean ownerOnly) public boolean setReadable(boolean readable) //修改文件寫權限 public boolean setWritable(boolean writable, boolean ownerOnly) public boolean setWritable(boolean writable) //修改文件可執行權限 public boolean setExecutable(boolean executable, boolean ownerOnly) public boolean setExecutable(boolean executable) 複製代碼

在修改方法中,若是修改爲功,返回true,不然返回false。在設置權限方法中,ownerOnly爲true表示只針對owner,爲false表示針對全部用戶,沒有指定ownerOnly的方法中,ownerOnly至關因而true。

文件操做

文件操做主要有建立、刪除、重命名。

建立

新建一個File對象不會實際建立文件,但以下方法能夠:

public boolean createNewFile() throws IOException 複製代碼

建立成功返回true,不然返回false,新建立的文件內容爲空。若是文件已存在,不會建立。

File對象還有兩個靜態方法,能夠建立臨時文件:

public static File createTempFile(String prefix, String suffix) throws IOException public static File createTempFile(String prefix, String suffix, File directory) throws IOException 複製代碼

臨時文件的完整路徑名是系統指定的、惟一的,但能夠經過參數指定前綴(prefix)、後綴(suffix)和目錄(directory),prefix是必須的,且至少要三個字符,suffix若是爲null,則默認爲".tmp", directory若是不指定或指定爲null,則使用系統默認目錄。咱們看個例子:

File file = File.createTempFile("upload_", ".jpg");
System.out.println(file.getAbsolutePath());
複製代碼

在個人電腦上的一些運行的輸出爲:

/var/folders/fs/8s4jdbj51jvcm7vc6lm_144r0000gn/T/upload_8850973909847443784.jpg
複製代碼

刪除

File類以下刪除方法:

public boolean delete() public void deleteOnExit() 複製代碼

delete刪除文件或目錄,刪除成功返回true,不然返回false。若是File是目錄且不爲空,則delete不會成功,返回false,換句話說,要刪除目錄,先要刪除目錄下的全部子目錄和文件。

deleteOnExit將File對象加入到待刪列表,在Java虛擬機正常退出的時候進行實際刪除。

重命名

方法爲:

public boolean renameTo(File dest) 複製代碼

參數dest表明重命名後的文件,重命名可否成功與系統有關,若是成功返回true,不然返回false。

目錄操做

當File對象表明目錄時,能夠執行目錄相關的操做,如建立、遍歷。

建立

有兩個方法用於建立目錄:

public boolean mkdir() public boolean mkdirs() 複製代碼

它們都是建立目錄,建立成功返回true,失敗返回false。須要注意的是,若是目錄已存在,返回值是false。這兩個方法的區別在於,若是某一箇中間父目錄不存在,則mkdir會失敗,返回false,而mkdirs則會建立必需的中間父目錄。

遍歷

有以下方法訪問一個目錄下的子目錄和文件:

public String[] list()
public String[] list(FilenameFilter filter)
public File[] listFiles()
public File[] listFiles(FileFilter filter)
public File[] listFiles(FilenameFilter filter)
複製代碼

它們返回的都是直接子目錄或文件,不會返回子目錄下的文件。list返回的是文件名數組,而listFiles返回的是File對象數組。FilenameFilter和FileFilter都是接口,用於過濾,FileFilter的定義爲:

public interface FileFilter {
    boolean accept(File pathname);
}
複製代碼

FilenameFilter的定義爲:

public interface FilenameFilter {
    boolean accept(File dir, String name);
}
複製代碼

在遍歷子目錄和文件時,針對每一個文件,會調用FilenameFilter或FileFilter的accept方法,只有accept方法返回true時,纔將該子目錄或文件包含到返回結果中。

FilenameFilter和FileFilter的區別在於,FileFilter的accept方法參數只有一個File對象,而FilenameFilter的accept方法參數有兩個,dir表示父目錄,name表示子目錄或文件名。

咱們來看個例子,列出當前目錄下的全部後綴爲.txt的文件,代碼能夠爲:

File f = new File(".");
File[] files = f.listFiles(new FilenameFilter(){
    @Override
    public boolean accept(File dir, String name) {
        if(name.endsWith(".txt")){
            return true;
        }
        return false;
    }
});
for(File file : files){
    System.out.println(file.getCanonicalPath());
}
複製代碼

咱們建立了個FilenameFilter的匿名內部類對象並傳遞給了listFiles。

使用遍歷方法,咱們能夠方便的進行遞歸遍歷,完成一些更爲高級的功能。

好比,計算一個目錄下的全部文件的大小(包括子目錄),代碼能夠爲:

public static long sizeOfDirectory(final File directory) {
    long size = 0;
    if (directory.isFile()) {
        return directory.length();
    } else {
        for (File file : directory.listFiles()) {
            if (file.isFile()) {
                size += file.length();
            } else {
                size += sizeOfDirectory(file);
            }
        }
    }
    return size;
}
複製代碼

再好比,在一個目錄下,查找全部給定文件名的文件,代碼能夠爲:

public static Collection<File> findFile(final File directory, final String fileName) {
    List<File> files = new ArrayList<>();
    for (File f : directory.listFiles()) {
        if (f.isFile() && f.getName().equals(fileName)) {
            files.add(f);
        } else if (f.isDirectory()) {
            files.addAll(findFile(f, fileName));
        }
    }
    return files;
}
複製代碼

前面介紹了File類的delete方法,咱們提到,若是要刪除目錄而目錄不爲空,須要先清空目錄,利用遍歷方法,咱們能夠寫一個刪除非空目錄的方法,代碼能夠爲:

public static void deleteRecursively(final File file) throws IOException {
    if (file.isFile()) {
        if (!file.delete()) {
            throw new IOException("Failed to delete "
                    + file.getCanonicalPath());
        }
    } else if (file.isDirectory()) {
        for (File child : file.listFiles()) {
            deleteRecursively(child);
        }
        if (!file.delete()) {
            throw new IOException("Failed to delete "
                    + file.getCanonicalPath());
        }
    }
}
複製代碼

小結

本節介紹瞭如何在Java中利用File類進行文件和目錄操做,File類封裝了操做系統和文件系統的差別,提供了統一的API。

理解了這些操做,咱們回過頭來,再看下文件內容的操做,前面咱們介紹的都是流,除了流,還有其餘操做方式,如隨機訪問和內存映射文件,爲何還須要這些方式?它們有什麼特色?適用於什麼場合?讓咱們接下來繼續探索。


未完待續,查看最新文章,敬請關注微信公衆號「老馬說編程」(掃描下方二維碼),深刻淺出,老馬和你一塊兒探索Java編程及計算機技術的本質。用心原創,保留全部版權。

相關文章
相關標籤/搜索