4

次のコードがあります

public static void main(String[] args) throws UnsupportedEncodingException {
        System.out.println(Charset.defaultCharset().toString());

        String accentedE = "é";

        String utf8 = new String(accentedE.getBytes("utf-8"), Charset.forName("UTF-8"));
        System.out.println(utf8);
        utf8 = new String(accentedE.getBytes(), Charset.forName("UTF-8"));
        System.out.println(utf8);
        utf8 = new String(accentedE.getBytes("utf-8"));
        System.out.println(utf8);
        utf8 = new String(accentedE.getBytes());
        System.out.println(utf8);
}

上記の出力は次のとおりです

windows-1252
é
?
é
é

誰かがこれが何をするのか理解するのを手伝ってくれますか? なぜこの出力?

4

3 に答える 3

6

すでに を持っている場合はString、それをエンコードしてデコードする必要はありません。文字列は、生のバイトをデコードした結果です。

文字列リテラルの場合、誰かがソースを生のバイトとして読み取り、指定したエンコーディングでデコードするコンパイラです。ソース ファイルを Windows-1252 エンコーディングで物理的に保存し、コンパイラがそれを Windows-1252 としてデコードする場合、すべて問題ありません。そうでない場合は、ソースをコンパイルするときにコンパイラが使用する正しいエンコーディングを宣言して、これを修正する必要があります...

この線

String utf8 = new String(accentedE.getBytes("utf-8"), Charset.forName("UTF-8"));

絶対に何もしません。(UTF-8 としてエンコード、UTF-8 としてデコード == no-op)

この線

utf8 = new String(accentedE.getBytes(), Charset.forName("UTF-8"));

文字列を Windows-1252 としてエンコードしてから、UTF-8 としてデコードします。結果は Windows-1252 でのみデコードする必要があります (Windows-1252でエンコードされているためです)。そうしないと、奇妙な結果が得られます。

この線

utf8 = new String(accentedE.getBytes("utf-8"));

文字列を UTF-8 としてエンコードし、Windows-1252 としてデコードします。前の場合と同じ原則が適用されます。

この線

utf8 = new String(accentedE.getBytes());

絶対に何もしません。(Windows-1252 としてエンコード、Windows-1252 としてデコード == no-op)

理解しやすいかもしれない整数との類推:

int a = 555;
//The case of encoding as X and decoding right back as X
a = Integer.parseInt(String.valueOf(a), 10);
//a is still 555

int b = 555;
//The case of encoding as X and decoding right back as Y
b = Integer.parseInt(String.valueOf(b), 15);
//b is now 1205 I.E. strange result

コードを実行する前に必要なものである integer が既にあるため、これらはどちらも役に立ちません555

文字列がシステムから出るときに文字列を raw バイトにエンコードする必要があり、システムに入るときに raw バイトを文字列にデコードする必要があります。システム内でエンコードおよびデコードする必要はありません。

于 2013-03-19T13:24:31.513 に答える
1

1 行目 - システムのデフォルトの文字セットは windows-1252 です。

2 行目 - 文字列リテラルを UTF-8 バイトにエンコードし、UTF-8 スキームを使用してデコードすることで文字列を作成しました。結果は正しく形成された文字列であり、windows-1252 エンコーディングを使用して正しく出力できます。

3 行目 - 文字列リテラルを Windows-1252 としてエンコードし、UTF-8 を使用してデコードして文字列を作成しました。UTF-8 デコーダーは、UTF-8 ではない可能性のあるシーケンスを検出し、問題のある文字を疑問符 "?" に置き換えました。(UTF-8 形式では、最上位ビットが 1 に設定されているバイトはマルチバイト文字の 1 バイトであると言われています。しかし、windows-1252 エンコーディングは 1 バイトの長さです....つまり、これは悪い UTF-です。 8)

4 行目 - UTF-8 でエンコードし、windows-1252 でデコードして文字列を作成しました。この場合、デコードは「失敗」していませんが、ガベージ (文字化け) が生成されています。2 文字の出力が得られた理由は、"é" の UTF-8 エンコーディングが 2 バイト シーケンスであるためです。

行 #5 - windows-1252 としてエンコードし、windows-1252 としてデコードすることにより、文字列を作成しました。これにより、正しい出力が生成されます。


そして、全体的な教訓は、ある文字エンコーディングで文字をバイトにエンコードし、次に別の文字エンコーディングでデコードすると、何らかの形式のマングリングが発生する可能性があるということです。

于 2013-03-19T13:30:29.327 に答える
0

String getBytesメソッドを呼び出すと、次のようになります。

プラットフォームのデフォルトの文字セットを使用して、この文字列を一連のバイトにエンコードし、結果を新しいバイト配列に格納します。

だからあなたがするときはいつでも:

accentedE.getBytes()

あなたの場合はcp-1252で、デフォルトのOSコードページでエンコードされたバイトとしてaccentedE Stringの内容を取ります。

この行:

new String(accentedE.getBytes(), Charset.forName("UTF-8"))

(cp1252 でエンコードされた)accentedE バイトを取得し、UTF-8 でデコードしようとするため、エラーが発生します。反対側からの同じ状況:

new String(accentedE.getBytes("utf-8"))

getBytes メソッドは、cp-1252 でエンコードされたアクセント付き E バイトを取得し、UTF-8 で再エンコードしますが、Stringコンストラクターはデフォルトの OS コードページである cp-1252 でエンコードします。

プラットフォームのデフォルトの文字セットを使用して、指定されたバイト配列をデコードすることにより、新しい String を構築します。新しい文字列の長さは文字セットの関数であるため、バイト配列の長さと等しくない場合があります。

この優れた記事を読むことを強くお勧めします。

すべてのソフトウェア開発者が絶対に、積極的に Unicode と文字セットについて知っておく必要がある絶対最小値 (言い訳はありません!)

アップデート:

つまり、すべての文字が数値として格納されます。どの文字がどの番号であるかを知るために、OS はコードページを使用します。次のスニペットを検討してください。

String accentedE = "é";

System.out.println(String.format("%02X ", accentedE.getBytes("UTF-8")[0]));
System.out.println(String.format("%02X ", accentedE.getBytes("UTF-8")[1]));
System.out.println(String.format("%02X ", accentedE.getBytes("windows-1252")[0]));

出力:

C3 
A9 
E9

これは、UTF-8 の小さなアクセント付き e は2 バイトの value として格納されるのC3A9に対し、cp-1252では 1 バイトの value として格納されるためですE9。詳細な説明については、リンクされた記事をお読みください。

于 2013-03-19T13:24:56.760 に答える