Java 如何将文件系统路径映射到 Unicode?

问题描述 投票:0回答:1

在 Rust 中,操作系统文件路径使用特定的

Path
类型而不是
str
来存储。这是因为
str
表示 UTF-8 字节序列,而内核要么不强制执行任何类型的编码 (Unix),只要斜杠用其 ASCII 代码点表示,要么以 16 位编码进行编码( Windows)。

在Java中,路径使用标准库中的String类型来表示,该类型内部使用UCS-2。这不是特定于实现的,因为某些 String 方法会以某种方式“泄漏”此编码。

Java 如何将 Unix 的任意字节序列路径表示为 Unicode?我假设它将路径视为 ASCII 或 UTF-8,以便将“原始”字节映射到 Unicode 代码点,因为路径实际上几乎总是 ASCII 或 UTF-8,但是如果存在无效的字节序列怎么办ASCII 还是 UTF-8?到 Unicode/UCS-2 的转换是否无损?是否有可来回记录的算法?如果我不想排除非拉丁字母用户,在处理 Java 中的文件路径时我应该考虑什么?

示例

  • 进入空目录
  • 输入
    touch $(echo -e "\xc3\x28")
    。这将创建一个完全有效的文件,其名称无法用 Unicode 表示。
  • 打开
    jshell
  • 类型
    new File(".").listFiles()[0].isFile()
    • 这会返回
      false
    • 如果您查看列出的文件的路径,它包含一个 U+FFFD 代码点,后跟
      \x28
      ,这意味着
      \xC3
      已被有损地转换为 Unicode,不再代表实际路径
java unicode character-encoding ucs2
1个回答
1
投票

你的问题不清楚,但这里是。

我认为“非 unicode”实际上是指“不在 Java 的内部字符编码中”。

几乎不存在非 Unicode 字符。 Unicode 15.1 代表 149,813 个字符,几乎是人类使用的所有字符,包括学者和权威机构确定的过去和现在、“活着”和“死了”的语言和文字。加上一堆表情符号🤷🏽u200d♂️。随着越来越多的晦涩字符被识别出来,Unicode 也在不断增长。

Java 作为平台设计的一个主要方面是让您与主机操作系统的细节隔离。

其中包括有关主机操作系统所使用的文件系统的详细信息。如果您使用捆绑的 Java 类与文件交互(提示:NIO.2),那么您无需关心主机操作系统文件系统内部的位和字节。处理这些文件系统详细信息是 (a) 编写操作系统的人员和 (b) 编写 Java 实现(例如 OpenJDK)的人员的工作。 Java 的部分乐趣并不在于处理这些事情。

例如,我在 macOS Sonoma 14.4.1 的 Finder.app 中创建了嵌套的文件夹层次结构。我使用谷歌翻译翻译了俄语中的“苹果”,罗马尼亚语中的“香蕉”,日语中的“胡萝卜”。在该文件夹中,我创建了一个 UTF-8 编码的纯文本文件,名称为法语“data”。该文件包含一行:

Bonjour!

screenshot of macOS Finder.app window with hierarchy of folders in various languages

使用 Java NIO.2,我通过

Path
(复数)类用
Paths
对象表示该文件的路径。

final String HOME = System.getProperty( "user.home" );  // /Users/basil_dot_work for me on macOS.
final Path path = Paths.get( HOME, "Яблоко" , "Banană" , "にんじん" , "données.txt" ); // Apple in Russian, Banana in Romanian, Carrot in Japanese.

这里的重点是,我不知道苹果工程师如何在各个文件夹名称中对各种语言进行编码。我不在乎。 Java 代表我负责与 macOS 文件系统交互。 它的工作原理要感谢 Apple 开发人员和任何其他为 OpenJDK 项目 macOS 分支做出贡献的人。

我需要做的就是在我的 Java 代码的

.java

 源文件中以 UTF-8 编码表示 Unicode 字符。我的 
IDE (IntelliJ) 使用的编译器知道如何解析 UTF-8,然后编码为 Java 字节码。在运行时,JVM 会破译该字节码中的预期文本,以某种内部定义的编码来表示文本。 (顺便说一句,Java 的内部定义的编码不一定是 UCS-2。我依稀记得,现代 JVM 实现可能会使用更有效的东西来处理面向拉丁语的内容。同样,我不在乎 - 这就是 Java 的好处。)

接下来,我使用

Files.lines

 打开该文件来读取 Stream
 行,每行都是 
String
 对象。

try { Files .lines( path , StandardCharsets.UTF_8 ) .forEach( System.out :: println ); } catch ( IOException e ) { throw new RuntimeException( e ); }
在 Java 22 中运行时的结果:

你好!

现在我们可以将完全相同的编译类带到 Linux 机器上,并确信它将成功运行。 Linux 特有的 JVM 将知道如何在字节码编码的文件夹/文件名和 Linux 文件系统自身对这些相同 Unicode 字符的编码之间进行协调。

© www.soinside.com 2019 - 2024. All rights reserved.