在使用Oracle的Java 7时,File.list()在Mac OS X上错误地检索具有NON-ASCII字符的文件名

我在使用File.list()时遇到问题,当使用Oracle的Java 7时,文件名在Mac OS X上错误地检索到了非ASCII字符。

我使用以下示例:

import java.io.*; import java.util.*; public class ListFiles { public static void main(String[] args) { try { File folder = new File("."); String[] listOfFiles = folder.list(); for (int i = 0; i < listOfFiles.length; i++) { System.out.println(listOfFiles[i]); } Map env = System.getenv(); for (String envName : env.keySet()) { System.out.format("%s=%s%n", envName, env.get(envName)); } } catch (Exception e) { e.printStackTrace(); } } } 

使用Apple的Java 6运行此示例,一切都很好:

 .... Folder-ÄÖÜäöüß吃饭.txt .... 

使用Oracle的Java 7运行此示例,结果如下:

 .... Folder-A  O  U  a  o  u           .txt .... 

但是,如果我按如下方式设置环境(在上述两种情况下未设置):

 LANG=en_US.UTF-8 

Oracle的Java 7的结果如预期:

 .... Folder-ÄÖÜäöüß吃饭.txt .... 

我的问题是我不想设置LANG环境变量。 这是一个GUI应用程序,我想部署为Mac OS X应用程序,并且这样做,LSEnvironment设置

 LSEnvironment  LANG en_US.UTF-8  

在Info.plist中没有任何效果(另请参见此处 )

如何在Mac OS X上从Oracle正确检索文件名,而无需设置LANG环境? 在Windows和Linux中,此问题不存在。

编辑:

如果我打印单个字节:

 byte[] x = listOfFiles[i].getBytes(); for (int j = 0; j < x.length; j++) { System.out.format("%02X",x[j]); System.out.print(" "); } System.out.println(); 

正确的结果是:

 Folder-ÄÖÜäöüß 46 6F 6C 64 65 72 2D 41 CC 88 4F CC 88 55 CC 88 61 CC 88 6F CC 88 75 CC 88 C3 9F吃饭.txt E5 90 83 E9 A5 AD 2E 74 78 74 

错误的结果是:

 Folder-A  O  U  a  o  u     46 6F 6C 64 65 72 2D 41 EF BF BD EF BF BD 4F EF BF BD EF BF BD 55 EF BF BD EF BF BD 61 EF BF BD EF BF BD 6F EF BF BD EF BF BD 75 EF BF BD EF BF BD EF BF BD EF BF BD       .txt EF BF BD EF BF BD EF BF BD EF BF BD EF BF BD EF BF BD 2E 74 78 74 

因此,可以看到Files.list()用UTF-8替换一些字节“EF BF BD”= Unicode U + FFFD =替换字符,如果未设置LANG(仅来自Oracle的Java 7)。

如果其他一切都失败了,请为JVM创建一个包装器,用于设置LC_CTYPE环境变量,然后启动您的应用程序。 OS X不关心plist告诉它运行哪个程序呢? 在shell脚本中创建这个包装器可能最简单:

 #!/bin/bash export LC_CTYPE="UTF-8" # Try other options if this doesn't work exec java your.program.Here 

问题在于Java的方式 – 来自Apple或Oracle的任何Java版本 – 从文件系统中读取文件的名称。 文件系统上的文件名基本上是二进制数据,必须对它们进行解码才能在Java中将它们用作String。 (您可以在我的博客中阅读有关此问题的更多信息 。)

编码的检测因平台和版本而异,因此必须与Apple Java 6和Oracle Java 7不同:Java 6正确检测到系统设置为UTF-8,而Java 7则错误。

奇怪的是,当我尝试使用以下程序重现问题时,我发现Java 6和Java 7都正确地使用UTF-8来解码文件名(它们被正确打印到终端)。 对于其他I / O,Java 6u35使用MacRoman作为默认字符集,而Java 7u7使用UTF-8(由file.encoding系统属性显示)。

 import java.io.*; public class Test { public static void main(String[] args) { System.setOut(new PrintStream(System.out, true, "UTF-8")); System.out.println(System.getProperty("file.encoding")); for (File f: new File(".").listFiles) { System.out.println(g.getName()); } } } 

当我在OS 10.7上运行locale ,我得到了这个输出。 似乎在我的系统上,Java 6没有正确解释为LC_CTYPE指定的值。 据我所知,系统没有自定义,一切都设置为英文,所以这应该是默认配置:

 LANG= LC_COLLATE="C" LC_CTYPE="UTF-8" LC_MESSAGES="C" LC_MONETARY="C" LC_NUMERIC="C" LC_TIME="C" LC_ALL= 

从Java6运行得到正确的结果,这样:

 System.out.println(new String(listOfFiles[i].getBytes(),"UTF-8")); 

解决这个问题?

这个建议的构造函数显式地将listOfFiles [i]字符串解释为UTF-8编码的字符串。

编辑:

由于它不起作用,这意味着UTF-8不是os x的默认编码。 维基百科说Mac OS Roman是。 所以我建议尝试:

 System.out.println(new String(listOfFiles[i].getBytes(),"MacRoman")); 

但那应该是一样的

 System.out.println(new String(listOfFiles[i].getBytes())); 

因此,如果这也不起作用,那么可以得出结论,这可能是安德鲁汤姆森在评论你的问题时所说的错误。

这是OpenJDK中的一个已知错误。 OS X 10.6和OS X 10.7为默认语言环境返回不同的值。 请参阅错误http://java.net/jira/browse/MACOSX_PORT-204和http://java.net/jira/browse/MACOSX_PORT-165 。 如果您遇到此问题,请投票支持修复此问题。

将JDK降级为内置的Mac OSX JDK。 如果你这样做,问题应该消失。

此外,您可能还希望在Eclipse中将运行配置设置为以UTF-8运行。

这是旧的Java文件api中的一个错误(可能只是在mac上)。 无论如何,它都是在新的java.nio中修复的。

我有几个文件包含文件名中的unicode字符和无法使用java.io.File和相关类加载的内容。 转换我的所有代码后使用java.nio.Path EVERYTHING开始工作。 我用java.nio.Files替换了org.apache.commons.io.FileUtils(它有同样的问题)…

…并确保使用适当的字符集读取和写入文件内容,例如:Files.readAllLines(myPath,StandardCharsets.UTF_8)