web-dev-qa-db-ja.com

「範囲外」が「substring(startIndex、endIndex)」に対してスローされないのはなぜですか

Javaでsubstring()メソッドを使用していますが、「インデックスが不足しています」というエラーがスローされない理由がわかりません。

文字列abcdeのインデックスの開始は0〜4ですが、substring()メソッドは、foo.substring(0)を呼び出して "abcde 」.

では、なぜsubstring(5)が機能するのでしょうか?そのインデックスは範囲外である必要があります。説明は何ですか?

/*
1234
abcde
*/
String foo = "abcde";
System.out.println(foo.substring(0));
System.out.println(foo.substring(1));
System.out.println(foo.substring(2));
System.out.println(foo.substring(3));
System.out.println(foo.substring(4));
System.out.println(foo.substring(5));

このコードは以下を出力します:

abcde
bcde
cde
de
e
     //foo.substring(5) output nothing here, isn't this out of range?

5を6に置き換えると:

foo.substring(6)

次に、エラーが発生します。

Exception in thread "main" Java.lang.StringIndexOutOfBoundsException:
    String index out of range: -1
18
Meow

Java API doc によると、開始インデックスが文字列のLengthより大きい場合、部分文字列はエラーをスローします。

IndexOutOfBoundsException-beginIndexが負であるか、このStringオブジェクトの長さより大きい場合。

実際、それらはあなたの例とよく似ています。

"emptiness".substring(9) returns "" (an empty string)

これは、Java Stringを次のように考えるのが最善であることを意味します。ここで、インデックスは|

|0| A |1| B |2| C |3| D |4| E |5|

つまり、文字列には開始インデックスと終了インデックスの両方があります。

21
Matt Mitchell

foo.substring(5)を実行すると、 "e"の直後の位置から始まり、文字列の末尾で終わる部分文字列が取得されます。ちなみに、開始位置と終了位置は偶然同じです。したがって、空の文字列。インデックスは、文字列内の実際の文字ではなく、文字間の位置と考えることができます。

        ---------------------
String: | a | b | c | d | e |
        ---------------------
Index:  0   1   2   3   4   5
16
Jeff

これは、部分文字列関数が「包括的」部分文字列を返すためです。したがって、インデックス5は、文字列の最後の前の位置を指しますが、文字列の最後の表示文字の後です。

これはドキュメントに示されています: http://download.Oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/api/Java/lang/String.html#substring(int)

3
Josiah

String API javadocから:

public String substring(int beginIndex)
    Returns a new string that is a substring of this 
    string. The substring begins with the "" character 
    at the specified index and extends to the end of this string.

public String substring(int beginIndex, int endIndex)
    Returns a new string that is a substring of this 
    string. The substring begins at the specified beginIndex 
    and extends to the character at index endIndex - 1. Thus 
    the length of the substring is endIndex-beginIndex.

例:

"unhappy".substring(2) returns "happy" 
"Harbison".substring(3) returns "bison"
"emptiness".substring(9) returns "" (an empty string)

"hamburger".substring(4, 8) returns "urge"
"smiles".substring(1, 5) returns "mile"

パラメーター:

beginIndex - the beginning index, inclusive.
Returns:
the specified substring.
Throws:
IndexOutOfBoundsException - if beginIndex is negative or 
larger than the length of this String object.

====

これは仕様によるものです。文字列のサイズとしてインデックスを指定すると、空の文字列が返されます。

3
samitgaur

私はこのスレッドがかなり古いことを知っていますが、これは根本的な問題であり、明確にする必要があると思います。

質問は適切にスポットされています。これをJava String.substring(int beginIndex、int endIndex)メソッドのソフトウェア障害と見なします。

http://docs.Oracle.com/javase/7/docs/api/Java/lang/String.html#substring%28int,%20int%29

Javaドキュメントから https://docs.Oracle.com/javase/tutorial/Java/nutsandbolts/arrays.html

Java Arrays

Java/C/C++と私が知っている他のすべての言語は、配列要素間の「仕切り」として配列インデックスを表示しません。

パラメータ:beginIndex-開始インデックス。 endIndex-終了インデックス(これを含まない)。

最後の配列要素ORを含めるために必要なendIndex + 1のアドレスへのメモリアクセスが言語で許可されていないため、endIndexの名前が間違っています。endIndexは誤って定義されている必要があります。endIndex-終了インデックス。

最も可能性の高いケースは、2番目のパラメーターの名前が誤っていることです。長さ-beginIndexで始まる必要な文字列の長さ。

Goslingは、親しみやすいようにC/C++言語のJava構文に基づいていることを知っています。 C+++文字列クラスから http://www.cplusplus.com/reference/string/string/substr/ メソッドの定義は次のとおりです。

文字列substr(size_t pos = 0、size_t len = npos)const;

メソッド定義の2番目のパラメーターは、長さが「len」であることに注意してください。

lenサブストリングに含める文字数(ストリングが短い場合は、できるだけ多くの文字が使用されます)。

testStringには10文字があり、インデックス位置は0〜9です。endIndexに10を指定すると、testStringには10のendIndexがないため、常にIndexOutOfBoundsException()がスローされます。

C++メソッドを確認する具体的な値を使用してJUnitでメソッドをテストすると、次のことが期待されます。

文字列testString = "testString"; assertThat(testString.substring(4、6)、equalTo( "String"));

しかし、当然、「文字列」が期待されますが、「St」でした

インデックス0から「文字列」の文字「g」までのtestStringの長さは10文字です。 「endIndex」パラメータとして10を使用すると、

文字列testString = "testString"; assertThat(testString.substring(4、10)、equalTo( "String"));

JUnitの「パス」。

パラメータ2の名前を「lengthOfSubstringFromIndex0」に変更した場合、endIndex-1カウントを実行する必要はなく、endIndexを指定するときに予期されるIndexOutOfBoundsException()をスローすることはありません。これは、基になる配列の範囲外です。 http://docs.Oracle.com/javase/7/docs/api/Java/lang/IndexOutOfBoundsException.html

これは、このメソッドの特異性を覚えておかなければならない時の1つにすぎません。 2番目のパラメーターの名前が正しくありません。 Javaメソッドのシグネチャは次のようになります。

public String substring(int beginIndex,
           int lengthOfSubstringFromIndex0)

または、C++ string :: substrメソッドと一致するようにメソッドを再定義しました。もちろん、再定義することはインターネット全体を書き換えることを意味するので、そうなる可能性は低いです。

3
Ted Spradley

substring(5)は既存のインデックスを指しています...たまたま空の文字列を指しています。一方、substring(6)はおかしな話です。 :)

2
Faisal