6

我需要从用户输入的名称生成文件名。这些名称可以是任何语言。例如:

  • “约翰·史密斯”
  • 《高冈和子》
  • "محمد سعيد بن عبد العزيز الفلسطيني"

这些是使用输入的值,所以我不能保证名称不包含在文件名中无效的字符。

用户将从他们的浏览器下载这些文件,因此我需要确保文件名在所有配置的所有操作系统上都有效。

我目前正在为英语国家这样做,只需使用简单的正则表达式删除所有非字母数字字符:

string = string.replaceAll("[^a-zA-Z0-9]", "");
string = string.replaceAll("\\s+", "_")

一些示例转换:

  • “约翰史密斯”->“John_Smith.ext”
  • “约翰·奥亨利”->“John_OHenry.ext”
  • “约翰·范·史密斯三世”->“John_van_Smith_III.ext”

显然,这在国际上是行不通的。

我考虑过查找/生成在所有文件系统上无效的所有字符的黑名单,并从名称中删除这些字符。我一直找不到完整的清单。

如果可能的话,我更愿意使用公共库中的现有代码。我想这是一个已经解决的问题,但是我找不到在国际上有效的解决方案。

文件名是给下载文件的用户的,不是给我的。我不会存储这些文件。这些文件是由服务器根据数据库中数据的请求动态生成的。文件名是为了方便下载文件的人。

4

6 回答 6

4

正则表达式[^a-zA-Z0-9]将过滤非 ASCII 字符,这些字符将忽略 Unicode 字符或超过 128 个代码点的字符。

假设您要通过替换无效文件名字符(例如? \ / : | < > *用下划线 ( _) )来过滤用户输入的有效文件名:

import java.io.UnsupportedEncodingException;

public class ReplaceI18N {

    public static void main(String[] args) {
        String[] names = {
                "John Smith",
                "高岡和子",
                "محمد سعيد بن عبد العزيز الفلسطيني",                
                "|J:o<h>n?Sm\\it/h*", 
                "高?岡和\\子*", 
                "محمد /سعيد بن عبد ?العزيز :الفلسطيني\\"
                };

        for(String s: names){
            String u  = s;
            try {
                u = new String(s.getBytes(), "UTF-8");
            } catch (UnsupportedEncodingException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } 
            u = u.replaceAll("[\\?\\\\/:|<>\\*]", " "); //filter ? \ / : | < > *
            u = u.replaceAll("\\s+", "_");
            System.out.println(s + " = " + u);
        }
    }
}

输出:

John Smith = John_Smith
高岡和子 = 高岡和子
محمد سعيد بن عبد العزيز الفلسطيني = محمد_سعيد_بن_عبد_العزيز_الفلسطيني
|J:o<h>n?Sm\it/h* = _J_o_h_n_Sm_it_h_
高?岡和\子* = 高_岡和_子_
محمد /سعيد بن عبد ?العزيز :الفلسطيني\ = محمد_سعيد_بن_عبد_العزيز_الفلسطيني_

即使是 Unicode 字符,有效的文件名也可以显示在任何支持 UTF-8 编码和正确 Unicode 字体的网页上。

此外,在任何支持 Unicode 的操作系统文件系统上,每个文件都是其文件的正确名称(在 Windows XP、Windows 7 上测试正常)。

i18n 文件名

但是,如果您想将每个有效文件名作为 URL 字符串传递,请确保使用正确对其进行编码,URLEncoder然后使用URLDecoder.

于 2012-04-14T07:14:51.323 回答
0

让输入确定文件名而不进行适当的清理似乎容易受到安全攻击。您可以使用散列函数(SHA-1、MD5)来生成有效的文件名。请注意,您不能从哈希中派生原始名称。

此外,如果您有一个简单的查找表,您可以为名称分配特殊标识符(如序号或 GUID),并将标识符用作文件名。

另一件事,你有没有想过同音字?

于 2012-04-14T03:59:13.673 回答
0

将文件名编码为 UTF-8,然后对结果进行 URL 编码。

'高岡和子' -> '%E9%AB%98%E5%B2%A1%E5%92%8C%E5%AD%90'
于 2012-04-14T04:02:34.183 回答
0

Windows似乎支持 unicode 文件名,我知道 Linux 支持,显然 OS X 也支持。据推测,编写良好的文件会在保存文件名之前修复文件名中的无效字符。

似乎您应该能够只使用 unicode 文件名。是否有一些操作系统或浏览器无法运行?

于 2012-04-14T04:03:47.117 回答
0

我的建议是要求您的应用程序在支持 Unicode 文件名的平台上运行。这些天大多数人都这样做。

我认为从 Unicode 映射到(未指定的)受限字符集是不可行的,同时仍保留人类可读性和原始含义并避免冲突。事实上,甚至不可能进行从 Latin-1 到 ASCII 的映射。

如果您的应用程序必须在不支持 Unicode 文件名的平台上运行,那么在某些情况下,您将需要牺牲人类可读性和/或文件名的含义。此外,请考虑(例如)ASCII 化的中文字符或西里尔字母或去除重音的字母是否会被您的最终用户接受。


我要做的是为用户提供两个选项供您选择:

  • 使用 Unicode 文件名上传文件的选项。这应该是默认设置,因为大多数用户的机器都支持这一点。

  • 使用与原始字符串/文本无关的生成名称的后备选项。

实际上,如果用户的机器不支持 Unicode,那么他们将在处理未使用机器的本机编码编码的文本名称时遇到巨大的问题。没有完全可靠的方法可以找出那是什么。即使您有一种半可靠的方法来解决这个问题......在服务器端......将所有 Unicode 映射到该编码的问题是棘手的。

最好鼓励用户将他/她的操作系统升级到支持 Unicode 的操作系统。

于 2012-04-14T05:53:21.723 回答
0

总结和解释@eee的答案......

String sanitizeFilename(String unsanitized) {
     return unsanitized
                .replaceAll("[\\?\\\\/:|<>\\*]", " ") // filter out ? \ / : | < > *
                .replaceAll("\\s", "_");              // white space as underscores
}

(不要将多个空间合二为一!)

于 2013-03-20T09:54:12.967 回答