程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> 計算機程序的思維邏輯 (59),思維59

計算機程序的思維邏輯 (59),思維59

編輯:JAVA綜合教程

計算機程序的思維邏輯 (59),思維59


前面兩節我們介紹了如何通過流的方式讀寫文件內容,本節我們介紹文件元數據和目錄的一些操作。

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

File類中的操作大概可以分為三類:

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

在介紹這些操作之前,我們先來看下File的構造方法。

構造方法

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

public File(String pathname)
public File(String parent, String child)
public File(File parent, String child) 

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

File中的路徑可以是已經存在的,也可以是不存在的。

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

文件元數據

文件名與文件路徑

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

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編程及計算機技術的本質。用心原創,保留所有版權。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved