6

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 に設定し、file.encoding および sun.jnu.encoding プロパティをコマンド ラインから UTF-8 として指定すると、ガベージ出力が得られ、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 がファイル名を解釈する方法です。

Update 3 Java がファイル名を解釈する方法に問題があるようです。次の単純な C コードは、ファイル エンコーディングと LC_ALL 環境変数の値に関係なく、Linux で動作します (これがここで与えられた答えを証明することを嬉しく思います: https://unix.stackexchange.com/questions/39175/understanding-unix-file-名前エンコード)。しかし、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');
    }
}
4

3 に答える 3

10

注:最終的に、私はそれを釘付けにしたと思います。それが正しいことを確認しているわけではありません。しかし、いくつかのコードを読んでテストすると、これが私が見つけたものであり、それを調べる時間がありません。誰かが興味を持っている場合は、チェックして、この答えが正しいか間違っているかを確認してください - 私はうれしいです:)

私が使用した参照は、OpenJDK のサイトで入手可能なこの tarball からのものでした: openjdk-6-src-b25-01_may_2012.tar.gz

  1. Java は、次のメソッドですべての文字列をプラットフォームのローカル エンコーディングにネイティブに変換します: jdk/src/share/native/common/jni_util.c - JNU_GetStringPlatformChars(). システム プロパティsun.jnu.encodingは、プラットフォームのエンコーディングを決定するために使用されます。

  2. の値は、libc のメソッドを使用してsun.jnu.encoding設定されます。環境変数は、の値を設定するために使用されます。Java のオプションを使用してコマンド プロンプトで指定された値は無視されます。jdk/src/solaris/native/java/lang/java_props_md.c - GetJavaProperties()setlocale()LC_ALLsun.jnu.encoding-Dsun.jnu.encoding

  3. への呼び出しFile.exists()はファイルにコーディングされており、次jdk/src/share/classes/java/io/File.javaのように返されます

    return ((fs.getBooleanAttributes(this) & FileSystem.BA_EXISTS) != 0);

  4. getBooleanAttributes()jdk/src/share/native/java/io/UnixFileSystem_md.cin function : でネイティブにコード化されています (そして、多くのファイルを参照するコードの手順をスキップしています) Java_java_io_UnixFileSystem_getBooleanAttributes0()。ここで、マクロは WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path)パス文字列をプラットフォームのエンコーディングに変換します。

  5. そのため、間違ったエンコーディングに変換すると、実際には間違った C 文字列 (char 配列) が後続のstat()メソッド呼び出しに送信されます。そして、ファイルが見つからないという結果を返します。

教訓: LC_ALL非常に重要

于 2012-07-09T14:11:47.820 に答える
5

についてどこで読んだかわかりませんfile.encodingで文書化されているように、他の標準プロパティSystem.getPropertiesで言及されているとは思いません。しかし、私の実験から判断すると、この値はファイルではなく、ファイルコンテンツのエンコーディングに影響を与えるようです。特に、がの場合、非 ASCII 文字を出力しません。System.outfile.encodingPOSIX

一方、ファイル名にどのエンコーディングを適用するかを決定する Linux の方法LC_CTYPEは、現在のロケール設定の側面です。Java がこれをオーバーライドする理由がわかりません。他の多くのプラットフォーム (特に Windows) では、バイトではなく常にファイル名に Unicode を使用するため、ファイル システムのバイトレベルの詳細を Java アプリケーションに公開してもほとんど意味がありません。

于 2012-07-05T12:47:36.117 に答える