私はすでに次の投稿を読みました:
次に、以下のコードを考えます。
_public static void main(String[] args) {
printCharacterDetails("最");
}
public static void printCharacterDetails(String character){
System.out.println("Unicode Value for "+character+"="+Integer.toHexString(character.codePointAt(0)));
byte[] bytes = character.getBytes();
System.out.println("The UTF-8 Character="+character+" | Default: Number of Bytes="+bytes.length);
String stringUTF16 = new String(bytes, StandardCharsets.UTF_16);
System.out.println("The corresponding UTF-16 Character="+stringUTF16+" | UTF-16: Number of Bytes="+stringUTF16.getBytes().length);
System.out.println("----------------------------------------------------------------------------------------");
}
_
上記のコードでcharacter.getBytes()
の行をデバッグしようとすると、デバッガーがStringクラスのgetBytes()
メソッドに移動し、続いてstatic byte[] encode(char[] ca, int off, int len)
メソッドに移動しましたStringCodingクラス。エンコードメソッドの最初の行(String csn = Charset.defaultCharset().name();
)は、デバッグ中にデフォルトのエンコードとして「UTF-8」を返しました。 「UTF-16」だと思っていました。
プログラムの出力は次のとおりです。
最= 6700のUnicode値UTF-8文字=最|デフォルト:バイト数= 3
対応するUTF-16 Character =�| UTF-16:バイト数= 6
プログラムで明示的にUTF-16に変換すると、文字を表すのに6バイトかかりました。 UTF-16には2バイトまたは4バイトを使用しないでください。なぜ6バイトが使用されたのですか?
私の理解のどこが悪いのでしょうか? Ubuntu 14.04を使用していますが、localeコマンドで次のように表示されます。
_LANG=en_US.UTF-8
_
これは、JVMが基盤となるOSに基づいて使用するエンコーディングを決定することを意味しますか、それともUTF-16のみを使用しますか?コンセプトを理解させてください。
キャラクターは、人間の文化の一部であるグラフィカルなエンティティです。コンピュータがテキストを処理する必要がある場合、バイト単位のそれらの文字の表現を使用します。使用される正確な表現はencodingと呼ばれます。
同じ文字を表すことができる多くのエンコーディングがあります-Unicode文字セット、またはさまざまなISO-8859エンコーディングやJIS X0208などの他の文字セットを介して。
内部的には、JavaはUTF-16を使用します。これは、各文字が2バイトの1つまたは2つのシーケンスで表されることを意味します。使用していた文字、最、コードポイントU + 6700 UTF-16では、バイト0x67およびバイト0x00として表されます。
これが内部エンコーディングです。メモリをダンプして、ダンプされたイメージのバイトを調べない限り、それを見ることができません。
しかし、メソッドgetBytes()
はこの内部表現を返しませんnot。そのドキュメントは言う:
public byte[] getBytes()
プラットフォームのデフォルトの文字セットを使用して、この
String
を一連のバイトにエンコードし、結果を新しいバイト配列に格納します。
「プラットフォームのデフォルトの文字セット」は、ロケール変数が言うとおりです。つまり、_UTF-8
_です。つまり、UTF-16の内部表現を受け取り、それを別の表現-UTF-8に変換します。
ご了承ください
_new String(bytes, StandardCharsets.UTF_16);
_
notは、想定どおりに「明示的にUTF-16に変換」します。この文字列コンストラクターは、2番目の引数で指定したエンコードに含まれているはずのバイトのシーケンスを受け取り、それらのバイトがそのエンコードで表す文字のUTF-16表現に変換します。
しかし、UTF-8でエンコードされた一連のバイトを与え、それをUTF-16として解釈するように指示しました。これは間違っており、期待する文字(またはバイト)が得られません。
Java内部で文字列を保存する方法はわかりません。文字列は常にUTF-16として保存されます。コンストラクタString(byte[],Charset)
はJava指定された文字セットに含まれるはずのバイト配列からUTF-16文字列を作成します。メソッドgetBytes(Charset)
は、Javaに、バイトのシーケンスを提供するように指示します。与えられたエンコーディング(charset)で文字列を表す引数なしのgetBytes()
メソッドも同じことを行いますが、変換にはプラットフォームのデフォルトの文字セットを使用します。
だからあなたはgetBytes()
があなたに与えるものを誤解しました。それはではなく内部表現です。直接入手することはできません。 getBytes(StandardCharsets.UTF_16)
だけがそれを提供し、_UTF-16
_がJavaの内部表現であることを知っているからです。 Javaの将来のバージョンで、別のエンコーディングで文字を表すことにした場合、getBytes(StandardCharsets.UTF_16)
は内部表現を表示しません。
編集:実際、Java 9は、文字列の内部表現にこのような変更を導入しました。デフォルトでは、文字がすべてISO-8859に該当する文字列です。 1つの範囲は内部的にISO-8859-1で表されますが、その範囲外の少なくとも1文字の文字列は以前と同様に内部的にUTF-16で表されます。実際、getBytes(StandardCharsets.UTF_16)
は内部表現を返しません。
上記のように、Javaは文字データのエンコーディングとしてUTF-16を使用します。
さらに、表現可能な文字のセットは、Unicode文字セット全体の適切なサブセットに限定されます。 (Javaは、その文字セットをUnicode BMPに制限し、すべてが2バイトのUTF-16に収まると思います。)
そのため、適用されるエンコードは確かにUTF-16ですが、適用される文字セットはUnicode文字セット全体の適切なサブセットであり、これによりJavaは常にトークンごとに2バイトを使用します内部の文字列エンコーディング。