web-dev-qa-db-ja.com

JavaはUTF-8またはUTF-16を使用しますか?

私はすでに次の投稿を読みました:

  1. Javaの文字列の内部表現は何ですか?変更されたUTF-8?UTF-16?
  2. https://docs.Oracle.com/javase/8/docs/api/Java/lang/String.html

次に、以下のコードを考えます。

_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のみを使用しますか?コンセプトを理解させてください。

8
Nitin Bhardwaj

キャラクターは、人間の文化の一部であるグラフィカルなエンティティです。コンピュータがテキストを処理する必要がある場合、バイト単位のそれらの文字の表現を使用します。使用される正確な表現は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)は内部表現を返しません。

15
RealSkeptic

上記のように、Javaは文字データのエンコーディングとしてUTF-16を使用します。

さらに、表現可能な文字のセットは、Unicode文字セット全体の適切なサブセットに限定されます。 (Javaは、その文字セットをUnicode BMPに制限し、すべてが2バイトのUTF-16に収まると思います。)

そのため、適用されるエンコードは確かにUTF-16ですが、適用される文字セットはUnicode文字セット全体の適切なサブセットであり、これによりJavaは常にトークンごとに2バイトを使用します内部の文字列エンコーディング。

2
Erwin Smout