【发布时间】:2012-07-05 19:52:11
【问题描述】:
在以下使用 OpenJDK 1.6.0_22 在 Linux 中运行的 Java 程序中,我简单地列出了在命令行中作为参数获取的目录的内容。该目录包含具有 UTF-8 文件名的文件(例如印地语、普通话、德语等)。
import java.io.*;
class ListDir {
public static void main(String[] args) throws Exception {
//System.setProperty("file.encoding", "en_US.UTF-8");
System.out.println(System.getProperty("file.encoding"));
File f = new File(args[0]);
for(String c : f.list()) {
String absPath = args[0] + "" + c;
File cf = new File(args[0] + "/" + c);
System.out.println(cf.getAbsolutePath() + " --> " + cf.exists());
}
}
}
如果我将 LC_ALL 变量设置为 en_US.UTF-8,则结果打印良好。但是,如果我将 LC_ALL 变量设置为 POSIX 并从命令行以 UTF-8 格式提供 file.encoding 和 sun.jnu.encoding 属性,我会得到垃圾输出并且 cf.exists() 返回 false。
你能解释一下这种行为吗?正如我在许多网站上阅读的那样,据说 file.encoding 足以读取文件名并将它们用于操作。在这里,该属性似乎根本没有效果。
更新 1: 如果我将 file.encoding 设置为 GBK(中文),并将 LC_ALL 变量设置为 en_US.UTF-8,则 cf.exists() 返回 true。只有 '?'出现而不是文件名。惊喜o_O。
更新 2: 进行更多调查,看起来这不是 Java 问题。看起来 Linux 上的 libc 使用语言环境设置来转换文件名编码,这些设置将导致找不到文件错误/异常。 "file.encoding" 用于说明 Java 如何解释文件名。
更新 3 现在看来问题在于 Java 如何解释文件名。无论文件编码和 LC_ALL 环境变量的值如何,以下简单的 C 代码都可以在 Linux 上运行(我很高兴这证明了这里给出的答案:https://unix.stackexchange.com/questions/39175/understanding-unix-file-name-encoding)。但我仍然不清楚 Java 如何解释 LC_ALL 变量。现在研究 OpenJDK 代码。
示例 C 代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
char *argdir = argv[1];
DIR *dp = opendir(argdir);
struct dirent *de;
while(de = readdir(dp)) {
char *abspath = (char *) malloc(strlen(argdir) + 1 + strlen(de->d_name) + 1);
strcpy(abspath, argdir);
abspath[strlen(argdir)] = '/';
strcpy(abspath + strlen(argdir) + 1, de->d_name);
printf("%d %s ", de->d_type, abspath);
FILE *fp = fopen(abspath, "r");
if (fp) {
printf("Success");
}
fclose(fp);
putchar('\n');
}
}
【问题讨论】:
-
我不确定到底是谁负责解码命令行参数。可能是外壳,也可能是 Java。如果您从其他地方(字符串文字,或从标准输入读取,或从属性文件)获取文件名,它是否按预期工作?
-
你为什么要跳
absPath舞?只是为了演示目的吗?您可以使用 f.listFiles() 而不是 f.list() 直接获取 File 对象(这可能有效)。 -
@Thilo 这只是一个示例程序。
-
@Thilo:问题中提到的
sun.jnu.encoding似乎会影响命令行解释。在LC_CTYPE=POSIX设置但使用 UTF-8 终端时,如果我通过-Dsun.jnu.encoding=UTF-8,来自命令行参数的非 ASCII 字符将打印为单个?,即使它是两个字节。如果我也通过-Dfile.encoding=UTF-8,那么它也会被打印为 UTF-8。如果我只通过后者,那么我会打印出两个 unicode 不可用的符号字形。
标签: java linux file encoding internationalization