如何在Java中打开包含重音符号的文件?

如何在Java中打开包含重音符号的文件?,java,unicode,character-encoding,Java,Unicode,Character Encoding,(编辑以澄清并添加一些代码) 你好,, 我们需要解析来自世界各地用户的数据。我们的Linux系统的默认语言环境为en_US.UTF-8。但是,我们经常收到名称中带有变音符号的文件,例如“特殊字符.doc”。尽管操作系统可以很好地处理这些文件,并且一个strace显示操作系统将正确的文件名传递给Java程序,但Java会咀嚼这些文件名,并抛出一个“未找到文件”io异常试图打开它们 这个简单的程序可以说明这个问题: import java.io.*; import java.text.*; pub

(编辑以澄清并添加一些代码)

你好,, 我们需要解析来自世界各地用户的数据。我们的Linux系统的默认语言环境为en_US.UTF-8。但是,我们经常收到名称中带有变音符号的文件,例如“
特殊字符.doc
”。尽管操作系统可以很好地处理这些文件,并且一个strace显示操作系统将正确的文件名传递给Java程序,但Java会咀嚼这些文件名,并抛出一个“未找到文件”io异常试图打开它们

这个简单的程序可以说明这个问题:

import java.io.*;
import java.text.*;

public class load_i18n
{
  public static void main( String [] args ) {
    File actual = new File(".");
    for( File f : actual.listFiles()){
      System.out.println( f.getName() );
    }
  }
}
在包含文件
specialāāāāācharacters.doc
的目录中运行此程序,默认的美式英语区域设置为:

特殊字符数½½½½½½字符数.doc

通过export LANG=es设置语言_ES@UTF-8正确地打印出文件名(但这是一个不可接受的解决方案,因为整个系统现在都是用西班牙语运行的。)在程序中显式地设置语言环境(如下所示)也没有效果。下面我将程序修改为a)尝试打开文件,b)在无法打开文件时以ASCII和字节数组打印名称:

import java.io.*;
import java.util.Locale;
import java.text.*;

public class load_i18n
{
  public static void main( String [] args ) {
    // Stream to read file
    FileInputStream fin;

    Locale locale = new Locale("es", "ES");
    Locale.setDefault(locale);
    File actual = new File(".");
    System.out.println(Locale.getDefault());
    for( File f : actual.listFiles()){
      try {
        fin = new FileInputStream (f.getName());
      }
      catch (IOException e){
        System.err.println ("Can't open the file " + f.getName() + ".  Printing as byte array.");
        byte[] textArray = f.getName().getBytes();
        for(byte b: textArray){
          System.err.print(b + " ");
        }
        System.err.println();
        System.exit(-1);
      }

      System.out.println( f.getName() );
    }
  }
}
这将产生输出

es_ES
load_i18n.class
Can't open the file special_�_�_�_characters.doc.  Printing as byte array.
115 112 101 99 105 97 108 95 -17 -65 -67 95 -17 -65 -67 95 -17 -65 -67 95 99 104 97 114 97 99 116 101 114 115 46 100 111 99
这表明问题不仅仅是控制台显示的问题,因为相同的字符及其表示形式是以字节或ASCII格式输出的。事实上,即使对某些实用程序(如bash的echo)使用LANG=en_US.UTF-8,控制台显示也可以工作:

[mjuric@arrhchadm30 tmp]$ echo $LANG
en_US.UTF-8
[mjuric@arrhchadm30 tmp]$ echo *
load_i18n.class special_á_ã_è_characters.doc
[mjuric@arrhchadm30 tmp]$ ls
load_i18n.class  special_?_?_?_characters.doc
[mjuric@arrhchadm30 tmp]$

是否可以修改此代码,使其在Linux下使用LANG=en_US.UTF-8运行时,读取文件名的方式能够成功打开?

Java系统属性
文件。编码应与控制台的字符编码匹配。在命令行上启动
java
时必须设置该属性:

java -Dfile.encoding=UTF-8 …

通常这会自动发生,因为控制台编码通常是平台默认编码,如果您没有明确指定,Java将使用平台默认编码。

首先,使用的字符编码与语言环境没有直接关系。因此,更改区域设置不会有多大帮助

其次,对于
以ISO-8859-1而不是UTF-8打印。这是一个证据:

System.out.println(new String("�".getBytes("UTF-8"), "ISO-8859-1")); // �
因此有两个问题:

  • JVM正在以
  • 您的控制台正在使用ISO-8859-1显示字符
  • 对于Sun JVM,VM参数
    -Dfile.encoding=UTF-8
    应该解决第一个问题。第二个问题将在控制台设置中修复。如果您正在使用例如Eclipse,您可以在窗口>首选项>常规>工作区>文本文件编码中更改它。也将其设置为UTF-8


    更新:根据您的更新:

    byte[] textArray = f.getName().getBytes();
    
    为排除平台默认编码的影响,应满足以下要求:

    byte[] textArray = f.getName().getBytes("UTF-8");
    

    如果这仍然显示相同,那么问题就更深了。你到底在用什么JVM?执行
    java-version
    。如前所述,
    -Dfile.encoding
    参数是特定于Sun JVM的。一些Linux机器附带GNU JVM或OpenJDK的JVM,这个参数可能不起作用。

    这是JRE/JDK中存在多年的一个bug


    我现在正在向他们重新提交一份新的bug报告,因为LC_ALL=en_us将修复一些案例,同时它将使其他一些案例失败。

    我整天都被这个问题所困扰! 我以前的(错误)代码与您相同:

    for(File f : dir.listFiles()) {
     String filename = f.getName(); // The filename here is wrong !
     FileInputStream fis = new FileInputStream (filename);
    }
    
    而且它不起作用(我在CentOS 6上使用Java 1.7 Oracle,除zimbra=>LANG和LC_CTYPE=C之外的所有用户都使用LANG和LC_CTYPE=fr_fr.UTF-8,顺便说一句,这肯定是导致此问题的原因,但如果zimbra没有停止工作的风险,我无法改变这一点…)

    因此我决定使用java.nio.file包的新类(文件和路径):

    DirectoryStream path=Files.newDirectoryStream(path.get(outputName));
    for(Iterator Iterator=path.Iterator();Iterator.hasNext();){
    Path=iterator.next();
    String filename=path.getFileName().toString();//此处的文件名是正确的
    ...
    }
    
    所以,如果您使用的是Java1.7,那么您应该尝试将新类添加到Java.nio.file包中:它节省了我的时间


    希望它对DirectoryStream的使用有所帮助,然后别忘了关闭流(请尝试使用参考资料,这里可以提供帮助)

    这是旧的skool java文件api中的一个bug,可能只是在mac上?无论如何,新的java.nioapi工作得更好。我有几个包含unicode字符的文件无法使用java.io加载。。。上课。在将所有代码转换为使用后,一切都开始工作。我将apache FileUtils(也有同样的问题)替换为…

    您的示例没有显示您试图打开这些文件,只需打印名称即可。Java是否能够打开文件,以及您的标准输出控制台(与Java无关)是否能够正确呈现字符,这是两件截然不同的事情。向我们展示给出IOException的代码,并给出IOException的详细信息和stacktrace。在这里查看推荐使用Java系统属性(user.language、user.country、user.variant)的答案:对不起,我从来没有打开过文件。调用FileInputStream将失败,因为我无法向其传递正确的文件名。文件“specialïï½ï½ï½ï½ïcharacters.doc”不存在。“specialèáèèèu characters.doc”文件确实有,但我的目录迭代从未列出过。谢谢Lauri。我尝试了所有这些技巧,但没有一个奏效。实际上,我在其中一次运行期间运行了一个strace(Linux),操作系统正在将正确的文件名传递给Java,但是当Java解释从getdents()系统调用传递的内容时,它会被破坏。下面是来自strace的相关系统调用:21993 getdents64(3,{…{d_ino=119,d_off=1
    DirectoryStream<Path> paths = Files.newDirectoryStream(Paths.get(outputName));
    for (Iterator<Path> iterator = paths.iterator(); iterator.hasNext();) {
      Path path = iterator.next();
      String filename = path.getFileName().toString(); // The filename here is correct
      ...
    }