如何在Java中打开包含重音符号的文件?
(编辑以澄清并添加一些代码) 你好,, 我们需要解析来自世界各地用户的数据。我们的Linux系统的默认语言环境为en_US.UTF-8。但是,我们经常收到名称中带有变音符号的文件,例如“如何在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
特殊字符.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
...
}