如何将任何语言和字符集中的字符串转换为Java中的有效文件名?

时间:2012-04-14 03:54:44

标签: java unicode internationalization filenames

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

  • “John Smith”
  • “高冈和子”
  • “更多信息”

这些是使用输入值,因此我无法保证名称不包含文件名无效的字符。

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

我目前正在为英语国家/地区执行此操作,只需使用简单的正则表达式删除所有非字母数字字符:

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

一些示例转化:

  • “John Smith” - > “John_Smith.ext”
  • “John O'Henry” - > “John_OHenry.ext”
  • “John van Smith III” - > “John_van_Smith_III.ext”

显然这在国际上不起作用。

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

如果可能的话,我更喜欢在公共库中使用现有代码。我想这是一个已经解决的问题,但我无法找到一个国际化的解决方案。

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

6 个答案:

答案 0 :(得分: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字符,有效的文件名也可以在支持使用正确Unicode字体的UTF-8编码的任何网页上显示。

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

i18n filenames

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

答案 1 :(得分:0)

让输入在没有正确清理的情况下确定文件名似乎容易受到安全攻击。您可以使用哈希函数(SHA-1,MD5)生成有效的文件名。请注意,您无法从哈希中获取原始名称。

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

另一件事,您是否考虑过同音异义词?

答案 2 :(得分:0)

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

'高岡和子' -> '%E9%AB%98%E5%B2%A1%E5%92%8C%E5%AD%90'

答案 3 :(得分:0)

Windows appears to support unicode file names,我知道Linux确实如此,显然OS X也是如此。据推测,编写良好的文件会在保存之前修复文件名中的无效字符。

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

答案 4 :(得分:0)

我的建议是将其作为要求,使您的应用程序在支持Unicode文件名的平台上运行。大多数人都这样做了。

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

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


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

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

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

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

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

答案 5 :(得分:0)

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

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

(不将多个空格合并为一个!)