【问题标题】:java.io.File: accessing files with invalid filename encodingsjava.io.File:访问文件名编码无效的文件
【发布时间】:2014-05-11 15:06:14
【问题描述】:

因为 java.io.File 的构造函数以 java.lang.String 作为参数,所以似乎不可能告诉它在访问文件系统层时期望使用哪种文件名编码。因此,当您通常使用 UTF-8 作为文件名编码并且有一些文件名包含编码为 ISO-8859-1 的变音符号时,您基本上是 **。这是正确的吗?

更新:因为似乎没有人得到它,所以自己尝试一下:创建新文件时,环境变量 LC_ALL(在 Linux 上)确定文件名的编码。 你在源代码中做了什么并不重要!

如果您想给出正确答案,请证明您可以使用正确的 ISO-8859-1 编码创建文件(使用常规 Java 方法),而您的 JVM 假定 LC_ALL=en_US.UTF-8。文件名应包含 ö、ü 或 ä 等字符。

顺便说一句:如果您将编码不适合 LC_ALL 的文件名放入 maven 的资源路径中,它将跳过它......

更新二。

修复此问题:https://github.com/jjYBdx4IL/filenameenc

即。使 f.exists() 语句变为真。

更新 III。

解决方案是使用 java.nio.*,在我的情况下,您必须将 File.listFiles() 替换为 Files.newDirectoryStream()。我已经更新了 github 上的示例。顺便说一句:maven 似乎仍在使用旧的 java.io API.... mvn clean 失败。

【问题讨论】:

  • file.encoding 确定读取文本文件时使用的默认字符集。它与文件名无关。
  • 另外,如果你使用Java 7+,你真的应该使用java.nio.file
  • 然后在 github 上查看我的测试用例。那肯定是错的。对于您的第二个建议:您真的希望使用 JDK 7 来从名称不好的文件中删除吗?
  • 你会因为许多其他原因想要使用 JDK7,比如 JDK6 不再被官方支持。

标签: java encoding character-encoding


【解决方案1】:

解决方案是使用新的 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的理由!

【讨论】:

  • 它已经证明了一件事:使用新 API,当字符串表示无法编码时,您无法创建路径。而你的文件名不能。
  • 你可以。您只需在 JVM 的不同运行之间切换您的区域设置。在 github 上看我的演示。
  • 不,你不能;你没看到上面的堆栈跟踪吗? (顺便说一句,任何 ISO 的 LC_ALL 都会产生 US-ASCII 作为字符集)
  • 查看我的更新...我在 file.encoding 上错了,但在 Path 上是正确的:它正确地完成了这项工作。
  • 避免从磁盘读取编码错误的文件名。当我不知道错误编码的名称时,我应该如何访问编码错误的文件名?
【解决方案2】:

目前我坐在一台 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 OutputStreamWriter(new FileOutputStream(fileName), "UTF-8")

    当您读取文件时,您可以将解码字符集作为流类构造函数。 InputStreamReader(InputStream in, CharsetDecoder dec)

    【讨论】:

    • 我说的是文件名,而不是文件的内容。
    【解决方案4】:

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

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

    new File("the file name with Ö")

    【讨论】:

    • 没有。字符串本身根本没有表示形式(如 UTF-8 等)。它可能有一个内部的,但作为一名程序员,这与你无关。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-18
    • 1970-01-01
    • 2021-05-13
    • 1970-01-01
    • 2016-11-20
    相关资源
    最近更新 更多