【发布时间】:2010-06-18 18:58:54
【问题描述】:
(编辑澄清并添加一些代码)
你好,
我们需要解析来自世界各地的用户发送的数据。我们的 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() );
}
}
}
在包含文件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() );
}
}
}
这会产生输出
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]$
是否可以修改此代码,使其在具有 LANG=en_US.UTF-8 的 Linux 下运行时,以可以成功打开的方式读取文件名?
【问题讨论】:
-
您的示例没有显示您尝试打开这些文件,只是打印名称。 Java 是否可以打开文件和你的标准输出控制台(与 Java 无关)是否可以正确渲染字符是两个非常不同的事情。向我们展示给出 IOException 的代码并给出 IOException 详细信息和堆栈跟踪。
-
在此处查看推荐使用 Java 系统属性(user.language、user.country、user.variant)的答案:stackoverflow.com/questions/64038/setting-java-locale-settings
-
对不起 - 我从来没有达到打开文件的地步。调用 FileInputStream 会失败,因为我无法将文件的正确名称传递给它。文件“special_�_�_�_characters.doc”不存在。文件“special_á_ã_è_characters.doc”确实如此,但我的目录迭代从未列出。
-
谢谢劳里。我尝试了所有这些技巧,但没有一个奏效。我实际上在其中一次运行期间运行了一个 strace (Linux),并且操作系统将正确的文件名传递给 Java,但是当 Java 解释从 getdents() 系统调用传递的内容时,它会被破坏。这是来自 strace 的相关系统调用: 21993 getdents64(3, {... {d_ino=119, d_off=1692303532, d_type=DT_REG, d_reclen=48, d_name="special_á_ã_è_characters.doc"} ... }, 4096) = 704 当 Java 读取该文件并将其传递给函数以打开文件时,它会尝试打开不存在的“special_�_�_�_characters.doc”。
-
Mark J,Mark P 的意思是,你并没有证明你不能将正确的文件名传递给 open 调用;您证明您无法将其打印到控制台。我或多或少愿意保证 'f.getName()' 返回正确的文件名;问题出在 println (以及您的控制台目标和编码),而不是 listFiles()。
标签: java unicode character-encoding