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

当从Oracle使用Java 7时,File.list()在Mac OS X上错误地检索带有非ASCII字符的文件名,java,macos,utf-8,filenames,Java,Macos,Utf 8,Filenames,当使用Oracle的Java 7时,我在Mac OS X上使用File.list()和带有非ASCII字符的文件名时遇到问题 我使用以下示例: import java.io.*; import java.util.*; public class ListFiles { public static void main(String[] args) { try { File folder = new File("."); String[] listOf

当使用Oracle的Java 7时,我在Mac OS X上使用File.list()和带有非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<String, String> env = System.getenv();
      for (String envName : env.keySet()) {
        System.out.format("%s=%s%n",
            envName,
            env.get(envName));
      }
    } catch (Exception e) { 
      e.printStackTrace(); 
    } 
  }

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

....
Folder-A��O��U��a��o��u����
������.txt
....
但是,如果我按如下方式设置环境(在上述两种情况下未设置):

Oracle Java 7的结果与预期一致:

....
Folder-ÄÖÜäöüß
吃饭.txt
....
我的问题是我不想设置LANG环境变量。这是一个GUI应用程序,我想将其部署为Mac OS X应用程序,这样做,LSEnvironment设置

<key>LSEnvironment</key>
<dict>
  <key>LANG</key>
  <string>en_US.UTF-8</string>
</dict>
错误的结果是:

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 

因此,我们可以看到,如果未设置LANG(只有Oracle的Java 7),则Files.list()会用UTF-8“EF BF BD”=Unicode U+FFFD=替换字符替换某些字节。

因为从Java6运行会得到正确的结果,这会:

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

这建议将listOfFiles[i]字符串显式解释为UTF-8编码字符串

编辑:


由于它不工作,这意味着UTF-8不是OSX的默认编码。但维基百科说这是事实。因此,我建议尝试:

System.out.println(new String(listOfFiles[i].getBytes(),"MacRoman"));
但这应该是最重要的


因此,如果这也不起作用,那么就会得出结论,这可能是一个bug,正如Andrew Thomson在对您的问题的评论中所述。

如果其他一切都失败了,请为JVM创建一个包装器,设置LC_CTYPE环境变量,然后启动您的应用程序。OSX不在乎plist让它运行哪个程序,是吗?在shell脚本中创建此包装器可能是最简单的:

#!/bin/bash
export LC_CTYPE="UTF-8" # Try other options if this doesn't work
exec java your.program.Here
问题在于Java(来自苹果或Oracle的任何Java版本)从文件系统读取文件名的方式。文件系统上的文件名本质上是二进制数据,必须对其进行解码才能在Java中将其用作字符串。(你可以在我的博客上找到。)

编码的检测因平台和版本而异,因此这一定是Apple Java 6和Oracle Java 7的不同之处:Java 6正确地检测到系统设置为UTF-8,而Java 7则错误

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

当我在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=

这是OpenJDK中的一个已知错误。OS X 10.6和OS X 10.7为默认区域设置返回不同的值。请参阅bug和。如果您有这个问题,请投票支持修复。

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


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

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

我有几个文件的文件名和内容中包含unicode字符,无法使用java.io.File和相关类加载。在将所有代码转换为使用后,一切都开始工作。我将org.apache.commons.io.FileUtils(也有同样的问题)替换为


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

有趣的问题,+1。你查过了吗?是的,我找到了。错误报告的结论是:已关闭,不是缺陷。我发现苹果的Java和Oracle在OS X以外的其他平台上的Java都没有这种行为,这很有趣。我刚刚测试了这一点,我发现了相反的问题:苹果的Java 6u35没有使用正确的编码,而Oracle的Java 7u7可以工作。您的区域设置是什么?在终端中运行
locale
;我将
CTYPE
设置为
UTF-8
,其他所有设置都设置为
C
LANG
LC\u ALL
未设置。如果我在终端内运行此程序,在所有情况下一切都正常,因为LANG始终设置为en\u US.UTF-8。据我所知,当把Java程序作为应用程序包运行时,LANG没有设置,LANG也不能设置(见我原来文章的末尾)。这个问题最终由Oracle在Java 7u40中解决了。请看,我很快就发布了它,但我确实打算首先进行编辑以给出解释和链接:)。你还认为它是一个注释候选吗?
System.out.println(新字符串(listOfFiles[i].getBytes(),“UTF-8”)无效。结果是
Folder-A��O��U��A.��o��U���� ...
.UTF-8不是mac os x的默认编码。@Andrew感谢您的输入:)我的一般回答标准是它必须包含至少一些研究,并且我在应答中发布代码时总是从我的机器上运行代码。“我同意我最初的回答形式更多的是一种评论,但我一开始就不想这样说。”linski Cool。在阅读了更新的编辑后,我决定投赞成票。但是为了绝对正确,我推测了这个bug,而OP发现了它并发布了链接(+1)。如果您试图复制我的示例,请删除所有LANG和LC_xxx环境变量(当您启动OSX应用程序包时的情况)。如果您在带有LANG=en_US.UTF-8或LANG=de_de.UTF-8的终端上运行它,那么我的示例可以在Apple的Java或Oracle的Java中正确运行。我创建了一个应用程序包装器,它调用一个bash脚本,该脚本设置环境并最终调用
System.out.println(new String(listOfFiles[i].getBytes(),"UTF-8"));
System.out.println(new String(listOfFiles[i].getBytes(),"MacRoman"));
System.out.println(new String(listOfFiles[i].getBytes()));
#!/bin/bash
export LC_CTYPE="UTF-8" # Try other options if this doesn't work
exec java your.program.Here
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());
    }
  }
}
LANG=
LC_COLLATE="C"
LC_CTYPE="UTF-8"
LC_MESSAGES="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_ALL=