簡體   English   中英

是否有跨平台的 Java 方法來刪​​除文件名特殊字符?

[英]Is there a cross-platform Java method to remove filename special chars?

我正在制作一個跨平台的應用程序,它根據在線檢索的數據重命名文件。 我想清理我從當前平台的 Web API 中獲取的字符串。

我知道不同的平台有不同的文件名要求,所以我想知道是否有跨平台的方式來做到這一點?

編輯:在 Windows 平台上,您不能有問號“?” 在文件名中,而在 Linux 中,您可以。 文件名可能包含這些字符,我希望支持這些字符的平台保留它們,否則,將它們去掉。

此外,我更喜歡不需要第三方庫的標准 Java 解決方案。

正如其他地方所建議的,這通常不是您想要做的。 通常最好使用諸如 File.createTempFile() 之類的安全方法來創建臨時文件。

您不應該使用白名單來執行此操作,而應僅保留“好”字符。 如果文件僅由中文字符組成,那么您將刪除所有內容。 出於這個原因,我們不能使用白名單,我們必須使用黑名單。

Linux 幾乎允許任何可能是真正痛苦的事情。 我只是將 Linux 限制在與您限制 Windows 相同的列表中,這樣您將來就可以省去麻煩。

在 Windows 上使用這個 C# 片段我生成了一個在 Windows 上無效的字符列表。 此列表中的字符比您想象的要多得多 (41),因此我不建議您嘗試創建自己的列表。

        foreach (char c in new string(Path.GetInvalidFileNameChars()))
        {
            Console.Write((int)c);
            Console.Write(",");
        }

這是一個“清理”文件名的簡單 Java 類。

public class FileNameCleaner {
final static int[] illegalChars = {34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47};
static {
    Arrays.sort(illegalChars);
}
public static String cleanFileName(String badFileName) {
    StringBuilder cleanName = new StringBuilder();
    for (int i = 0; i < badFileName.length(); i++) {
        int c = (int)badFileName.charAt(i);
        if (Arrays.binarySearch(illegalChars, c) < 0) {
            cleanName.append((char)c);
        }
    }
    return cleanName.toString();
}
}

編輯:正如斯蒂芬建議的那樣,您可能還應該驗證這些文件訪問是否僅發生在您允許的目錄中。

以下答案包含用於在 Java 中建立自定義安全上下文然后在該“沙箱”中執行代碼的示例代碼。

您如何創建安全的 JEXL(腳本)沙箱?

或者只是這樣做:

String filename = "A20/B22b#öA\\BC#Ä$%ld_ma.la.xps";
String sane = filename.replaceAll("[^a-zA-Z0-9\\._]+", "_");

結果: A20_B22b_A_BC_ld_ma.la.xps

解釋:

[a-zA-Z0-9\\\\._]匹配 az 小寫或大寫字母、數字、點和下划線

[^a-zA-Z0-9\\\\._]是相反的。 即所有與第一個表達式不匹配的字符

[^a-zA-Z0-9\\\\._]+是與第一個表達式不匹配的字符序列

因此,每個不包含 az、0-9 或 . _ 將被替換。

這是基於Sarel Botha接受的答案,只要您沒有遇到Basic Multilingual Plane之外的任何字符,它就可以正常工作。 如果您需要完整的 Unicode 支持(誰不需要?),請改用此代碼,這是 Unicode 安全的:

public class FileNameCleaner {
  final static int[] illegalChars = {34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47};

  static {
    Arrays.sort(illegalChars);
  }

  public static String cleanFileName(String badFileName) {
    StringBuilder cleanName = new StringBuilder();
    int len = badFileName.codePointCount(0, badFileName.length());
    for (int i=0; i<len; i++) {
      int c = badFileName.codePointAt(i);
      if (Arrays.binarySearch(illegalChars, c) < 0) {
        cleanName.appendCodePoint(c);
      }
    }
    return cleanName.toString();
  }
}

這里的主要變化:

  • 使用codePointCount icw length而不僅僅是length
  • 使用codePointAt而不是charAt
  • 使用appendCodePoint而不是append
  • 無需將char s 轉換為int s。 事實上,你永遠不應該處理char因為它們基本上被 BMP 之外的任何東西破壞了。

這是我使用的代碼:

public static String sanitizeName( String name ) {
    if( null == name ) {
        return "";
    }

    if( SystemUtils.IS_OS_LINUX ) {
        return name.replaceAll( "[\u0000/]+", "" ).trim();
    }

    return name.replaceAll( "[\u0000-\u001f<>:\"/\\\\|?*\u007f]+", "" ).trim();
}

SystemUtils來自Apache commons-lang3

有一個非常好的內置 Java 解決方案 - Character.isXxx()

嘗試Character.isJavaIdentifierPart(c)

String name = "name.é+!@#$%^&*(){}][/=?+-_\\|;:`~!'\",<>";
StringBuilder filename = new StringBuilder();

for (char c : name.toCharArray()) {
  if (c=='.' || Character.isJavaIdentifierPart(c)) {
    filename.append(c);
  }
}

結果是“name.é$_”。

從您的問題中不清楚,但是由於您計划接受來自網絡表單 (?) 的路徑名,您可能應該阻止重命名某些內容的嘗試; 例如“C:\\Program Files”。 這意味着您需要規范化路徑名以消除“.”。 和“..”,然后再進行訪問檢查。

鑒於此,我不會嘗試刪除非法字符。 相反,我會使用“new File(str).getCanonicalFile()”來生成規范路徑,接下來檢查它們是否滿足您的沙箱限制,最后使用“File.exists()”、“File.isFile()”等來檢查源和目標是否是 kosher 並且不是同一個文件系統對象。 我會通過嘗試執行操作並捕獲異常來處理非法字符。

Paths.get(...)拋出一個包含非法字符位置的詳細異常。

public static String removeInvalidChars(final String fileName)
{
  try
  {
    Paths.get(fileName);
    return fileName;
  }
  catch (final InvalidPathException e)
  {
    if (e.getInput() != null && e.getInput().length() > 0 && e.getIndex() >= 0)
    {
      final StringBuilder stringBuilder = new StringBuilder(e.getInput());
      stringBuilder.deleteCharAt(e.getIndex());
      return removeInvalidChars(stringBuilder.toString());
    }
    throw e;
  }
}

如果您想使用的不僅僅是 [A-Za-z0-9],請檢查MS Naming Conventions ,並且不要忘記過濾掉“...整數表示在 1 到 31 范圍內的字符,... ”,就像 Aaron Digulla 的例子一樣。 例如來自 David Carboni 的代碼對於這些字符是不夠的。

包含保留字符列表的摘錄:

使用當前代碼頁中的任何字符作為名稱,包括 Unicode 字符和擴展字符集 (128–255) 中的字符,但以下字符除外:

以下保留字符:

  • < (小於)
  • > (大於)
  • :冒號)
  • " (雙引號)
  • / (正斜杠)
  • \\ (反斜杠)
  • | (垂直條或管)
  • ? (問號)
  • * (星號)
  • 整數值零,有時稱為 ASCII NUL 字符。
  • 整數表示在 1 到 31 范圍內的字符,但允許這些字符的備用數據流除外。 有關文件流的更多信息,請參閱文件流。
  • 目標文件系統不允許的任何其他字符。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM