HOWTO · Java

如何在 Java 中獲取檔案的副檔名

本篇文章介紹瞭如何在 Java 中獲取檔案的副檔名。

本頁內容

副檔名是名稱中最後一個點後面的後綴,例如 student-records.pdf 中的 pdf。Java 的 File 和 String 都沒有專門取得副檔名的方法,所以你不是呼叫 Apache Commons IO 的 FilenameUtils 這類工具,就是自己用 String 方法找出最後一個點。以下示範這兩種做法,以及讓它們在完整路徑上保持正確的規則。

使用 Apache Commons IO 取得副檔名

FilenameUtils.getExtension() 會回傳檔名或路徑中最後一個點之後的文字。它已能辨識 / 和 \ 兩種分隔符號,所以目錄名稱中的點絕不會被誤認為副檔名;當名稱沒有可用的副檔名時,它會回傳 ""。

這個類別不屬於 JDK,因此請透過 Maven 或 Gradle 在專案中加入 commons-io 相依套件,或把 JAR 放到 classpath 上。加入相依套件後,匯入 org.apache.commons.io.FilenameUtils,並呼叫 String extension = FilenameUtils.getExtension(fileName);。當 fileName 設為 student-records.pdf 時,它會回傳 pdf。

由於編譯時需要這個外部函式庫,這個 API 呼叫以內嵌方式呈現;下方可執行的範例只使用 JDK。

使用 Java 字串方法取出副檔名

處理單純的檔名不需要任何函式庫:lastIndexOf('.') 找出最後一個點,substring() 取出它之後的所有內容。

執行這個範例:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

public class SimpleTesting {
  public static void main(String[] args) {
    String fileName = "student-records.pdf";
    int dot = fileName.lastIndexOf('.');
    String extension = dot > 0 ? fileName.substring(dot + 1) : "";
    System.out.println("File extension is : " + extension);
  }
}

輸出:

File extension is : pdf

dot > 0 這項檢查涵蓋兩種沒有副檔名可回傳的情況:readme 會產生 "",因為 lastIndexOf 回傳 -1;而像 .env 這樣以點開頭的名稱,會被視為名稱標記而非副檔名的分隔符號。這個簡短寫法看不出來的是,那個點是否落在名稱的目錄部分——這需要接下來能感知路徑的版本。程式碼與輸出區塊中的標籤文字(例如 File extension is :)依規定保留英文原文,顯示的是取出的副檔名。

處理路徑與邊界情況

在完整路徑上,副檔名的點必須位於最後一個路徑分隔符號之後。下面的輔助函式同時檢查 / 和 \,所以像 data.d 這樣帶點的資料夾不會滲進結果。遇到缺少點、以點開頭的名稱,以及以點結尾的名稱時,它都會回傳 "":

本節中的程式碼以及顯示的標籤或輸出仍為英文。

public class SimpleTesting {

  static String fileExtension(String path) {
    if (path == null) {
      return "";
    }
    int dot = path.lastIndexOf('.');
    int sep = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
    if (dot <= sep + 1 || dot == path.length() - 1) {
      return "";
    }
    return path.substring(dot + 1);
  }

  public static void main(String[] args) {
    String[] names = {
        "folder/s.gr/fg/student-records.pdf",
        "C:\\data.d\\reports\\summary.txt",
        "folder.d/README",
        "folder/.hidden",
        "archive.tar."
    };
    for (String name : names) {
      System.out.println(name + " -> '" + fileExtension(name) + "'");
    }
  }
}

輸出:

folder/s.gr/fg/student-records.pdf -> 'pdf'
C:\data.d\reports\summary.txt -> 'txt'
folder.d/README -> ''
folder/.hidden -> ''
archive.tar. -> ''

這個點是與 sep + 1 比較,而不只是 sep,所以位於最後一段名稱第一個位置的點同樣會得到 ""——這個輔助函式把 .hidden 視為沒有副檔名的隱藏檔。FilenameUtils.getExtension() 採用相同的分隔符號檢查,但在這裡做出不同選擇,對 .hidden 回傳 hidden;兩者對 folder.d/README 和 archive.tar. 都一致回傳 ""。請選用呼叫端所預期的慣例。

選擇合適的做法

當 Apache Commons IO 已經在你的 classpath 上,或你想用一次呼叫就得到經過充分測試的結果時,請使用 FilenameUtils.getExtension()——它內建就能處理兩種分隔符號和帶點的目錄。當你想要零相依、無法新增函式庫,或需要自訂規則(例如上面示範的開頭點行為)時,就改用 String 版本;此時分隔符號與結尾點的檢查就得由你自己負責。

對大多數已在做檔案 I/O 的專案來說,FilenameUtils.getExtension() 是更簡單、更安全的選擇。對於小型工具或受限的 classpath,這個能感知路徑的輔助函式用幾行程式碼就能涵蓋同樣的範圍。