0

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

有没有办法在 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 运行时环境(构建 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意味着?我认为

4

1 回答 1

0

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

可悲的是,没有适当的方法可以访问该字段。因此,我查看了 Path 类的可用方法和 sawtoUri方法,它从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 转换。但是,至少,它有效。

于 2021-03-25T16:26:42.527 回答