Java无法在文件名中打开具有代理Unicode值的文件?

时间:2009-10-09 19:21:39

标签: java file unicode filenames surrogate-pairs

我正在处理使用文件执行各种IO操作的代码,我希望能够处理国际文件名。我正在使用Java 1.5处理Mac,如果文件名包含需要代理的Unicode字符,则JVM似乎无法找到该文件。例如,我的测试文件是:

"草鷗外.gif"分为Java字符\u8349\uD85B\uDFF6\u9DD7\u5916.gif

如果我从这个文件名创建一个文件,我无法打开它,因为我得到一个FileNotFound异常。即使在包含该文件的文件夹上使用它也会失败:

File[] files = folder.listFiles(); 
for (File file : files) {
    if (!file.exists()) {
        System.out.println("Failed to find File"); //Fails on the surrogate filename
    }
}

我实际处理的大部分代码都是以下形式:

FileInputStream instream = new FileInputStream(new File("草鷗外.gif"));
// operations follow

我是否可以通过某种方式解决此问题,或者以不同方式转义文件名或打开文件?

4 个答案:

答案 0 :(得分:7)

我怀疑Java或Mac之一正在使用CESU-8而不是正确的UTF-8。 Java使用“修改过的UTF-8”(这是CESU-8的一个细微变化)用于各种内部目的,但我不知道它可以将它用作文件系统/ defaultCharset。不幸的是,我这里既没有Mac也没有Java来测试。

“修改”是一种改进的说法“严重错误”。而不是为补充(非BMP)字符输出四字节UTF-8序列,如?:

\xF0\xA6\xBF\xB6

它为每个代理输出一个UTF-8编码的序列:

\xED\xA1\x9B\xED\xBF\xB6

这不是一个有效的UTF-8序列,但无论如何很多解码器都会允许它。问题是,如果你通过一个真正的UTF-8编码器往返,你有一个不同的字符串,上面的四字节字符串。尝试访问具有该名称和繁荣的文件!失败。

首先让我们检查文件名实际存储在当前文件系统下的方式,使用一个平台,使用文件名的字节,如Python 2.x:

$ python
Python 2.x.something (blah blah)
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.listdir('.')

在我的文件系统(Linux,ext4,UTF-8)上,文件名“草?鸥外.gif”出现如下:

['\xe8\x8d\x89\xf0\xa6\xbf\xb6\xe9\xb7\x97\xe5\xa4\x96.gif']

这就是你想要的。如果这就是你得到的,那可能是Java做错了。如果你得到更长的六字节字符版本:

['\xe8\x8d\x89\xed\xa1\x9b\xed\xbf\xb6\xe9\xb7\x97\xe5\xa4\x96.gif']

可能OS X做错了...它总是存储这样的文件名吗? (或者这些文件最初来自其他地方?)如果将文件重命名为“正确”版本该怎么办?:

os.rename('\xe8\x8d\x89\xed\xa1\x9b\xed\xbf\xb6\xe9\xb7\x97\xe5\xa4\x96.gif', '\xe8\x8d\x89\xf0\xa6\xbf\xb6\xe9\xb7\x97\xe5\xa4\x96.gif')

答案 1 :(得分:4)

如果您的环境的默认语言环境不包含这些字符,则无法打开该文件。

请参阅:File.exists() fails with unicode characters in name

修改 好吧..你需要的是改变系统区域设置。无论你使用什么操作系统。

修改

请参阅:How can I open files containing accents in Java?

请参阅:JFileChooser on Mac cannot see files named by Chinese chars?

答案 2 :(得分:3)

这证明是Mac JVM的问题(在1.5和1.6上测试)。使用Java File类无法访问包含补充字符/代理项对的文件名。我最后用Carbon调用了一个JNI库来调用项目的Mac版本(ick)。我怀疑CESU-8问题bobince提到,因为JNI调用获取UTF-8字符返回了一个CESU-8字符串。看起来不像是你可以真正解决的问题。

答案 3 :(得分:0)

这是旧的skool java File api中的一个错误,也许只是在Mac上?无论如何,新的java.nio api工作得更好。我有几个文件包含无法使用java.io.File和相关类加载的unicode字符和内容。转换我的所有代码后使用java.nio.Path一切开始工作。我用java.nio.Files替换了org.apache.commons.io.FileUtils(它有同样的问题)......

...并确保使用适当的字符集读取和写入文件内容,例如:Files.readAllLines(myPath,StandardCharsets.UTF_8)