3

Java String(byte[]) コンストラクター (Java 6) のセマンティクスの背後にある理論的根拠を理解するのに苦労しています。結果の String オブジェクトの長さは、通常、間違っています。おそらく、ここにいる誰かが、これが理にかなっている理由を説明できるでしょう。

次の小さな Java プログラムを考えてみましょう。

import java.nio.charset.Charset;

public class Test {
    public static void main(String[] args) {
        String abc1 = new String("abc");
        byte[] bytes = new byte[32];

        bytes[0] = 0x61; // 'a'
        bytes[1] = 0x62; // 'b'
        bytes[2] = 0x63; // 'c'
        bytes[3] = 0x00; // NUL

        String abc2 = new String(bytes, Charset.forName("US-ASCII"));

        System.out.println("abc1: \"" + abc1 + "\" length: " + abc1.length());
        System.out.println("abc2: \"" + abc2 + "\" length: " + abc2.length());

        System.out.println("\"" + abc1 + "\" " +
                (abc1.equals(abc2) ? "==" : "!=") + " \"" + abc2 + "\"");
    }
}

このプログラムの出力は次のとおりです。

abc1: "abc" length: 3
abc2: "abc" length: 32
"abc" != "abc"

String byte[] コンストラクターのドキュメントには、「新しい String の長さは文字セットの関数であるため、バイト配列の長さと等しくない場合があります」と記載されています。実際、正確に言えば、US-ASCII 文字セットでは、文字列「abc」の長さは 32 ではなく 3 です。

不思議なことに、abc2 には空白文字が含まれていませんが、abc2.trim() は同じ文字列を返しますが、長さは正しい値 3 に調整され、abc1.equals(abc2) は true を返します...明らかに何か不足していますか?

はい、明示的な長さをコンストラクターに渡すことができることを認識しています。デフォルトのセマンティクスを理解しようとしているだけです。

4

4 に答える 4

15

Java では、文字列は null で区切られていません。バイト配列から構築される文字列は、配列の全長を使用します。0x00 は文字 に 1 対 1 で変換'\0'されるため、結果の文字列は配列全体と同じ長さ (32) になります。System.out に出力されると、null 文字は幅がゼロになるため、"abc" のように見えますが、実際には "abc\0\0\0..." (32 文字の場合) になります。

trim()これを修正する理由は、'\0'空白と見なされるためです。

String文字列のヌル区切りのバイト表現を に変換する場合は、停止するインデックスを見つける必要があることに注意してください。次に (@Brian がコメントで指摘しているように)、別の String コンストラクターを使用できます。

String abc2 = new String(bytes, 0, indexOfFirstNull, Charset.forName("US-ASCII"));

ただし、これは注意して行う必要があります。プラットフォームに US-ASCII 文字セットを使用しています。最初の 0 バイトのインデックスはおそらく自然な停止場所です。ただし、多くの文字セット (UTF-16 など) では、実際のテキストの通常の部分として 0 バイトが発生することがあります。

于 2012-10-04T14:53:34.007 に答える
5

結果の String オブジェクトの長さは、通常、間違っています。

いいえ、そうです - あなたはそれが何を意味するのかを誤解しているだけです. 基本的に、1バイトあたり1文字に基づいて文字列を作成しています-少なくともUS-ASCIIのエンコーディングを使用する場合。

不思議なことに、abc2 には空白文字が含まれていませんが、abc2.trim() は同じ文字列を返しますが、長さは正しい値 3 に調整され、abc1.equals(abc2) は true を返します...明らかに何か不足していますか?

状態のドキュメントtrim()(適用されない 2 つの条件の後):

  • それ以外の場合、コードが '\u0020' より大きい文字列の最初の文字のインデックスを k とし、コードが '\u0020' より大きい文字列の最後の文字のインデックスを m とします。インデックス k の文字で始まり、インデックス m の文字で終わるこの文字列の部分文字列を表す新しい String オブジェクトが作成されます。つまり、this.substring(k, m+1) の結果です。

したがって、trim()基本的に「空白」は「U+0000 から U+0020 を含む」と同等に扱われます。これは、「空白」の奇妙に不正確な (つまり、基本的に Unicode より前の) 表現ですが、動作を説明しています。

基本的にあなたが見ているものは次のとおりです。

String trailingNulls = "abc\0\0\0\0\0\0";
String trimmed = trailingNulls.trim();
System.out.println(trimmed.length()); // 3

これは、バイト配列から文字列を作成することとは関係ありません。

于 2012-10-04T14:55:22.693 に答える
0

まずはJavaでStringがObject型なので、Objectクラスのequals()メソッドで比較する..

例えば:

"abc" .equals("abc")

-\0メソッドを使用して結果の文字列からを削除するtrim()と、必要な結果が得られます....

于 2012-10-04T14:56:38.617 に答える
0

まず、割り当てられたインデックスが間違っています。彼らはする必要があります

        bytes[0] = 0x61; // 'a'
        bytes[1] = 0x62; // 'b'
        bytes[2] = 0x63; // 'c'
        bytes[3] = 0x00; // NUL

equals授業の方法を調べてStringみるとその理由がわかります。char[]インデックスの場合は、各値を反復してチェックしています。したがって、長さが異なる場合char[]は返されますfalse.

  while (n-- != 0) {
                if (v1[i++] != v2[j++])
                    return false;
            }

修正は使用することですtrim

 abc2.equals(abc1.trim())

String#trim()の Java ドキュメント

それ以外の場合、コードが '\u0020' より大きい文字列の最初の文字のインデックスを k とし、コードが '\u0020' より大きい文字列の最後の文字のインデックスを m とします。

于 2012-10-04T14:58:47.283 に答える