【问题标题】:How to find files when their names can be in different encodings?当文件的名称可以采用不同的编码时,如何查找文件?
【发布时间】:2021-06-21 05:29:28
【问题描述】:

我有一堆文件名用cp1251 编码。 我还有一堆带有utf8 编码名称的文件。 我需要一种方法来用 Java 代码找到它们。 另外,我无法使用convmv Linux 工具更改名称,因为有些旧系统也使用这些文件。

有没有办法在 Java 中的文件或路径实用程序方法中传递编码?

如果我现在使用 Files.walk 并尝试查看文件名,它们就会被破坏并且看起来像一堆 ??????????并且无法恢复(或者我找不到这样做的方法)。

代码:

Files.list(Paths.get("/data/my_input"))
   .forEach(path1 -> System.out.println(path1.getFileName()));

将输出:

asdasd.txt
download.jpeg
���� ����� � ������� ���������.txt

???...文件的真实名称是:тест файла с русскими символами.txt

系统语言环境是:

locale
LANG=en_US.UTF-8
LANGUAGE=en_US
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=ru_RU.UTF-8
LC_TIME=ru_RU.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=ru_RU.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=ru_RU.UTF-8
LC_NAME=ru_RU.UTF-8
LC_ADDRESS=ru_RU.UTF-8
LC_TELEPHONE=ru_RU.UTF-8
LC_MEASUREMENT=ru_RU.UTF-8
LC_IDENTIFICATION=ru_RU.UTF-8
LC_ALL=

JVM 使用-Dfile.encoding=UTF-8 运行

如果我这样做 ls | iconv -f "cp1251" -t "utf8" 我明白了:

asdasd.txt
download.jpeg
тест файла с русскими символами.txt

ls输出与java输出相同。

更新: @JosefZ 的建议链接也无效。

例子:

name=���� ����� � ������� ���������.txt
fffd fffd fffd fffd 0020 fffd fffd fffd fffd fffd 0020 fffd 0020 fffd fffd fffd fffd fffd fffd fffd 0020 fffd fffd fffd fffd fffd fffd fffd fffd fffd 002e 0074 0078 0074 

正如我们所见,它只是 fffd - 所以名字被销毁了。

代码:

try (DirectoryStream<Path> dir = Files.newDirectoryStream(Paths.get("/data/my_input/"))) {
    for (Path child : dir) {
        String filename = child.getFileName().toString();

        System.out.println("name=" + filename);
        for (char c : filename.toCharArray()) {
            System.out.printf("%04x ", (int) c);
        }
        System.out.println();
    }
}

我的 Java 版本(在链接中建议它是 jvm 错误): java版本“1.8.0_201” Java(TM) SE 运行时环境 (build 1.8.0_201-b09) Java HotSpot(TM) 64 位服务器 VM(内部版本 25.201-b09,混合模式)

更新 2: @skomisa 的建议无效。

代码:

PrintStream ps = new PrintStream(System.out, true, "UTF-8");      
Files.list(Paths.get("/data/my_input/")).forEach(path1 -> ps.println(path1.getFileName()));

结果:

asdasd.txt
download.jpeg
���� ����� � ������� ���������.txt

如果我打印出我们可以看到的文件名的字节,那么如果我们执行path.getFileName(),我们会得到一个破坏的名称。 代码:

Files.list(Paths.get("/data/my_input/")).forEach(path1 -> System.out.println(Arrays.toString(path1.getFileName().toString().getBytes(StandardCharsets.UTF_8))));

结果:

[97, 115, 100, 97, 115, 100, 46, 116, 120, 116]
[100, 111, 119, 110, 108, 111, 97, 100, 46, 106, 112, 101, 103]
[-17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, 32, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, 32, -17, -65, -67, 32, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, 32, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67, 46, 116, 120, 116]

-17, -65, -67 表示? 我认为

【问题讨论】:

  • @JosefZ 感谢您的建议。我已经看到了这个问题,不幸的是,它没有 java 解决方案。
  • 你试过System.console().writer().println(path1.getFileName());而不是System.out.println()吗?那是ls 的输出(没有到iconv 的管道)?
  • @JosefZ 不,我还没有尝试过你的建议。纯ls输出与java输出相同。

标签: java encoding utf-8 filenames cp1251


【解决方案1】:

我发现sun.nio.fs.UnixPath 中有byte[] path,其中包含未更改状态的原始文件名字节。如果我接受它并转换为cp1251,我将得到带有西里尔字符的正确名称:тест файла с русскими символами.txt

遗憾的是,没有适当的方法可以访问该字段。 所以,我查看了 Path 类的可用方法,看到了 toUri 方法,它从 path 字段中获取值。

有一个解决方案有点奏效:

Path tryToFindWithCp1251Encoding(Path directory, String filePathToSearch) throws IOException {
    try (Stream<Path> paths = Files.walk(directory)) {
        for (Iterator<Path> it = paths.iterator(); it.hasNext(); ) {
            Path path = it.next();

            // Using getRawPath method to exclude Uri prefix like "file:///"
            String uriString = path.toUri().getRawPath();

            // The "+" sign is a special character when decoding from url-encoded strings,
            // so we need to replace it by hand on "%2B".
            // See https://stackoverflow.com/a/6926987/2530910 (also look at the comments)
            uriString = uriString.replace("+", "%2B");

            String decodedFilePathFromCp1251 = URLDecoder.decode(uriString, "Cp1251");

            if (decodedFilePathFromCp1251.equals(filePathToSearch)) {
                return path;
            }
        }
        return null;
    }
}

这是一个相当老套的解决方案,我更喜欢使用一种更干净、更正确的方法来完成这项工作,而无需中间 URI 转换。但是,至少,它有效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-11
    • 1970-01-01
    相关资源
    最近更新 更多