File.listFiles()使用JDK 6修改unicode名称(Unicode规范化问题)

Jam*_*rty 36 java unicode normalization file-encodings unicode-normalization

在OS X和Linux上列出Java 6中的目录内容时,我正在努力解决一个奇怪的文件名编码问题:这些File.listFiles()和相关的方法似乎以不同于系统其他部分的编码方式返回文件名.

请注意,不仅仅是这些文件名的显示导致了我的问题.我主要感兴趣的是将文件名与远程文件存储系统进行比较,因此我更关心名称字符串的内容而不是用于打印输出的字符编码.

这是一个演示程序.它创建一个带有Unicode名称的文件,然后打印出从直接创建的文件中获取的文件名的URL编码版本,以及在父目录下列出的相同文件(您应该在空目录中运行此代码).结果显示该File.listFiles()方法返回的不同编码.

String fileName = "Trîcky Nåme";
File file = new File(fileName);
file.createNewFile();
System.out.println("File name: " + URLEncoder.encode(file.getName(), "UTF-8"));

// Get parent (current) dir and list file contents
File parentDir = file.getAbsoluteFile().getParentFile();
File[] children = parentDir.listFiles();
for (File child: children) {
    System.out.println("Listed name: " + URLEncoder.encode(child.getName(), "UTF-8"));
}
Run Code Online (Sandbox Code Playgroud)

这是我在系统上运行此测试代码时得到的结果.请注意%CC%C3角色的表示.

OS X Snow Leopard:

File name: Tri%CC%82cky+Na%CC%8Ame
Listed name: Tr%C3%AEcky+N%C3%A5me

$ java -version
java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02-279-10M3065)
Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01-279, mixed mode)
Run Code Online (Sandbox Code Playgroud)

KUbuntu Linux(在同一OS X系统上的VM中运行):

File name: Tri%CC%82cky+Na%CC%8Ame
Listed name: Tr%C3%AEcky+N%C3%A5me

$ java -version
java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1)
OpenJDK Client VM (build 16.0-b13, mixed mode, sharing)
Run Code Online (Sandbox Code Playgroud)

我曾尝试过各种黑客获得字符串的同意,包括设置file.encoding系统属性和各种LC_CTYPELANG环境变量.什么都没有帮助,我也不想诉诸这些黑客.

此(有点相关?)问题不同,我能够从列出的文件中读取数据,尽管奇怪的名字

Ste*_*n P 16

使用Unicode,有多种有效的方式来表示相同的字母.您在Tricky名称中使用的字符是"带有抑扬符号的拉丁文小写字母"和"带有上方环的拉丁文小写字母".

你说"注意%CC%C3字符表示",但仔细观察你看到的是序列

i 0xCC 0x82 vs. 0xC3 0xAE
a 0xCC 0x8A vs. 0xC3 0xA5
Run Code Online (Sandbox Code Playgroud)

也就是说,第一个是字母,i后跟0xCC82,它是Unicode\u0302 "组合旋律重音"字符的UTF-8编码,而第二个是UTF-8,用于\u00EE"带有抑扬符号的拉丁文小写字母".类似地,对于另一对,第一个是字母,a后面是0xCC8A,"上面的组合环"字符,第二个是"带有上面的环的拉丁小写字母".这两种都是有效的Unicode字符串的有效UTF-8编码,但一种是"组合",另一种是"分解"格式.

OS X HFS Plus卷将字符串(例如文件名)存储为"完全分解".Unix文件系统实际上是根据文件系统驱动程序选择存储它来存储的.您不能跨不同类型的文件系统进行任何一揽子陈述.

有关组合与分解形式的一般性讨论,请参阅Wikipedia关于Unicode等效性的文章,其中特别提到了OS X.

有关转换表单的信息,请参阅Apple的Tech Q&A QA1235(遗憾地在Objective-C中).

一个最近的电子邮件主题 Apple的Java开发邮件列表上可能是对你有所帮助.

基本上,您需要将分解的表单规范化为组合表单,然后才能比较字符串.