java.io.File:使用无效文件名编码访问文件

6
由于java.io.File的构造函数需要一个java.lang.String作为参数,所以似乎没有可能告诉它在访问文件系统层时应该期望什么文件名编码。因此,当您通常使用UTF-8作为文件名编码且某个文件名包含以ISO-8859-1编码的变音符号时,您基本上是**。这正确吗?
更新:因为似乎没有人理解,可以自己试试:在创建新文件时,环境变量LC_ALL(在Linux上)确定文件名的编码。 无论您在源代码中做什么都没有关系! 如果您想给出正确的答案,请演示如何使用常规Java手段创建具有正确ISO-8859-1编码的文件,而您的JVM假定LC_ALL = en_US.UTF-8。文件名应该包含像ö、ü或ä这样的字符。
顺便说一句:如果将编码不适合LC_ALL的文件名放入maven的资源路径中,它将被跳过....
更新II。
修复这个:https://github.com/jjYBdx4IL/filenameenc 即使f.exists()语句成为true。
更新III。
解决方案是使用java.nio.*,在我的情况下,必须将File.listFiles()替换为Files.newDirectoryStream()。我已经在github上更新了示例。顺便说一句:maven似乎仍在使用旧的java.io API... mvn clean失败。

1
file.encoding 确定在 读取文本文件 时使用的默认字符集。它与文件名无关。 - fge
另外,如果您使用Java 7+,建议使用java.nio.file。 - fge
那请查看我的 Github 上的测试用例。那绝对是错误的。至于您的第二个建议:您真的希望使用 JDK 7 来删除具有错误名称的文件吗? - user1050755
1
您可能希望使用JDK7的原因还有很多,比如JDK6不再得到官方支持。 - Karol S
5个回答

5
解决方案是使用新的API和file.encoding。演示:
fge@alustriel:~/tmp/filenameenc$ echo $LC_ALL
en_US.UTF-8
fge@alustriel:~/tmp/filenameenc$ cat Test.java
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Test
{

    public static void main(String[] args)
    {
        final String testString = "a/üöä";
        final Path path = Paths.get(testString);
        final File file = new File(testString);
        System.out.println("Files.exists(): " + Files.exists(path));
        System.out.println("File exists: " + file.exists());
    }
}
fge@alustriel:~/tmp/filenameenc$ install -D /dev/null a/üöä 
fge@alustriel:~/tmp/filenameenc$ java Test
Files.exists(): true
File exists: true
fge@alustriel:~/tmp/filenameenc$ java -Dfile.encoding=iso-8859-1 Test
Files.exists(): false
File exists: true
fge@alustriel:~/tmp/filenameenc$ 

File的理由又少了一个!


你可以这样做。只需要在不同的JVM运行之间切换你的区域设置即可。请查看我在Github上的演示。 - user1050755
不,你不能这样做;难道你没有看到上面的堆栈跟踪吗?(顺便说一下,将LC_ALL设置为任何ISO都会产生US-ASCII作为字符集) - fge
看到我的更新...我对file.encoding是错误的,但对于路径是正确的:它确实正常工作。 - fge
你避免从磁盘读取编码错误的文件名。当我不知道错误编码的名称时,我该如何访问编码错误的文件名? - user1050755
解决方案确实是使用java.nio.*,在我的情况下,你需要用Files.newDirectoryStream()替换File.listFiles()。 - user1050755
显示剩余2条评论

0

如何解决java.io.File(在Solaris 5.11上)的问题:

  • 在shell /全局中设置LC_*环境变量。

    例如:java -DLC_ALL="en_US.ISO8859-1"无效!

  • 确保系统安装了所设置的语言环境。

为什么这样可以解决问题?

Java内部调用nl_langinfo()来查找硬盘上路径的编码,但它不会注意通过-DVARNAME“为Java”设置的环境变量。

其次,如果例如LC_ALL设置的语言环境未安装,则会回退到C / ASCII。


0

目前我坐在一台Windows机器上,但假设你能够获取文件系统编码:

String encoding = System.getProperty("file.encoding");
String encoding = system.getEnv("LC_ALL");

然后您就有了检查文件名是否有效的手段。请注意:Windows可以表示Unicode文件名,而我的Linux当然使用UTF-8。

boolean validEncodingForFileName(String name) {
    try {
        byte[] bytes = name.getBytes(encoding);
        String nameAgain = new String(bytes, encoding);
        return name.equals(nameAgain); // Nothing lost?
    } catch (UnsupportedEncodingException ex) {
        return false; // Maybe true, more a JRE limitation.
    }
}

您可以尝试一下 File 是否足够聪明(我无法测试):

boolean validEncodingForFileName(String name) {
    return new File(name).getCanonicalPath().endsWith(name);
}

-3

字符串可以表示任何编码:

new File("the file name with \u00d6")

或者

new File("the file name with Ö")


2
不,一个字符串本身没有任何表示(如UTF-8等)。它可能有一个内部表示,但对于你作为程序员来说这并不重要。 - user1050755

-4

在读写文件时,您可以设置编码方式。例如,当您写入文件时,可以将编码方式传递给输出流写入器,如下所示:new OutputStreamWriter(new FileOutputStream(fileName), "UTF-8")

当您读取文件时,可以通过以下类构造函数指定解码字符集:InputStreamReader(InputStream in, CharsetDecoder dec)


3
我指的是文件名,而不是文件的内容。 - user1050755

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接