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

Mar*_*ric 13 java unicode character-encoding

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

您好,我们需要解析来自世界各地的用户发送的数据.我们的Linux系统具有en_US.UTF-8的默认语言环境.但是,我们经常会在名称中接收带有变音符号的文件,例如" special_á_ã_è_characters.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() );
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

在包含该文件special_á_ã_è_characters.doc和默认美国英语语言环境的目录中运行此程序可以:

special_�_�_�_characters.doc

通过export LANG = es_ES @ UTF-8设置语言正确打印出文件名(但这是一个不可接受的解决方案,因为整个系统现在以西班牙语运行.)如下所示,在程序中明确设置Locale也没有效果.下面我修改了程序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() );
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这会产生输出

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
Run Code Online (Sandbox Code Playgroud)

这表明该问题不仅仅是控制台显示的问题,因为相同的字符及其表示以字节或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]$
Run Code Online (Sandbox Code Playgroud)

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

Bal*_*usC 8

首先,使用的字符编码与语言环境没有直接关系.因此,更改区域设置无济于事.

其次,�典型的Unicode替换字符U + FFFD ?以ISO-8859-1而不是UTF-8打印.这是一个证据:

System.out.println(new String("?".getBytes("UTF-8"), "ISO-8859-1")); // �
Run Code Online (Sandbox Code Playgroud)

所以有两个问题:

  1. 您的JVM正在读取这些特殊字符?.
  2. 您的控制台正在使用ISO-8859-1来显示字符.

对于Sun JVM,VM参数-Dfile.encoding=UTF-8 应该解决第一个问题.第二个问题是在控制台设置中修复.如果您使用的是Eclipse,则可以在Window> Preferences> General> Workspace> Text File Encoding中更改它.也将它设置为UTF-8.


更新:根据您的更新:

byte[] textArray = f.getName().getBytes();
Run Code Online (Sandbox Code Playgroud)

这应该是以下排除平台默认编码的影响:

byte[] textArray = f.getName().getBytes("UTF-8");
Run Code Online (Sandbox Code Playgroud)

如果仍然显示相同,那么问题就更深了.你正在使用什么JVM?做一个java -version.如前所述,该-Dfile.encoding论点是Sun JVM特有的.有些Linux机器附带了GNU JVM或OpenJDK的JVM,这个参数可能不起作用.


eri*_*son 1

Java 系统属性file.encoding应与控制台的字符编码相匹配。该属性必须在启动时设置java在命令行

\n\n
java -Dfile.encoding=UTF-8 \xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n