Java 为什么不为“子字符串(开始索引,结束索引)”抛出“超出范围”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3233379/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:38:08  来源:igfitidea点击:

Why is "out of range" not thrown for 'substring(startIndex, endIndex)'

javasubstring

提问by Meow

In Java I am using the substring()method and I'm not sure why it is not throwing an "out of index" error.

在 Java 中,我正在使用该substring()方法,但我不确定为什么它不会抛出“索引不足”错误。

The string abcdehas index start from 0 to 4, but the substring()method takes startIndex and endIndex as arguments based on the fact that I can call foo.substring(0) and get "abcde".

该字符串的abcde索引从 0 到 4,但该substring()方法基于我可以调用 foo.substring(0) 并获得“abcde”这一事实,将 startIndex 和 endIndex 作为参数。

Then why does substring(5) work? That index should be out of range. What is the explanation?

那么为什么 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));

This code outputs:

此代码输出:

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

When I replace 5 with 6:

当我用 6 替换 5 时:

foo.substring(6)

Then I get error:

然后我得到错误:

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

采纳答案by Matt Mitchell

According to the Java API doc, substring throws an error when the start index is greater than the Lengthof the String.

根据Java API doc,当起始索引大于字符串的长度时,子字符串会引发错误。

IndexOutOfBoundsException - if beginIndex is negative or larger than the length of this String object.

IndexOutOfBoundsException - 如果 beginIndex 为负或大于此 String 对象的长度。

In fact, they give an example much like yours:

事实上,他们举了一个很像你的例子:

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

I guess this means it is best to think of a Java String as the following, where an index is wrapped in |:

我想这意味着最好将 Java 字符串视为以下内容,其中包含一个索引|

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

Which is to say a string has both a start and end index.

也就是说一个字符串既有开始索引又有结束索引。

回答by Faisal

substring(5) points to an existing index...it just happens to point to an empty string. substring(6), on the other hand, is just crazy talk. :)

substring(5) 指向一个现有的索引……它恰好指向一个空字符串。另一方面,substring(6) 只是胡说八道。:)

回答by Jeff

When you do foo.substring(5), it gets the substring starting at the position right after the "e" and ending at the end of the string. Incidentally, the start and end position happen to be the same. Thus, empty string. You can think of the index as being not an actual character in the string, but a position in between characters.

当你这样做时foo.substring(5),它会从“e”之后的位置开始并在字符串的末尾结束子字符串。顺便说一下,开始和结束位置碰巧是一样的。因此,空字符串。您可以将索引视为不是字符串中的实际字符,而是字符之间的位置。

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

回答by Josiah

It's because the substring function returns an "inclusive" substring. So the index 5 points to a location BEFORE the end of the string, but AFTER the last displaying character of the string.

这是因为 substring 函数返回一个“包含”子字符串。所以索引 5 指向字符串末尾之前的位置,但在字符串的最后一个显示字符之后。

This is shown in the documentation: http://download.oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/api/java/lang/String.html#substring(int)

这显示在文档中:http: //download.oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/api/java/lang/String.html#substring(int)

回答by samitgaur

From String API javadoc:

来自字符串 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.

Examples:

例子:

"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"

Parameters:

参数:

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

====

====

So this is by design. If you give the index as the size of the string, it returns empty string.

所以这是设计使然。如果将索引作为字符串的大小,则返回空字符串。

回答by Ted Spradley

I know this thread is quite old but this is such a fundamental problem that I think it warrants clarification.

我知道这个线程已经很老了,但这是一个如此基本的问题,我认为它值得澄清。

The question is properly spot on. I view this as a software fault in the Java String.substring(int beginIndex, int endIndex) method.

这个问题是正确的。我认为这是 Java String.substring(int beginIndex, int endIndex) 方法中的软件错误。

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

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

From the Java Docs https://docs.oracle.com/javase/tutorial/java/nutsandbolts/arrays.html

来自 Java 文档https://docs.oracle.com/javase/tutorial/java/nutsandbolts/arrays.html

Java Arrays

Java 数组

Java/C/C++ and every other language that I know of does NOT view the array index as the 'divider' between array elements.

Java/C/C++ 和我所知道的所有其他语言都不会将数组索引视为数组元素之间的“分隔符”。

Parameters: beginIndex - the beginning index, inclusive. endIndex - the ending index, exclusive.

参数: beginIndex - 开始索引,包括在内。endIndex - 结束索引,独占。

Either endIndex is misnamed because the language does not allow memory access to the address at endIndex + 1 which is required to include the last array element OR endIndex is mis-defined and must be: endIndex - the ending index, inclusive.

要么 endIndex 被错误命名,因为该语言不允许对 endIndex + 1 处的地址进行内存访问,该地址需要包括最后一个数组元素或 endIndex 被错误定义并且必须是: endIndex - 结束索引,包括在内。

The most likely case is that the second parameter was misnamed. It should be: length - the length of the string desired beginning at beginIndex.

最可能的情况是第二个参数命名错误。它应该是: length - 从 beginIndex 开始的所需字符串的长度。

We know that Gosling based the Java syntax on the C/C++ languages for familiarity. From C+++ string class http://www.cplusplus.com/reference/string/string/substr/we see the method definition is:

我们知道 Gosling 是基于 C/C++ 语言的 Java 语法来熟悉的。从 C+++ 字符串类http://www.cplusplus.com/reference/string/string/substr/我们看到方法定义是:

string substr (size_t pos = 0, size_t len = npos) const;

字符串 substr (size_t pos = 0, size_t len = npos) const;

Note that the second parameter in the method definition is 'len' for length.

请注意,方法定义中的第二个参数是长度的“len”。

len Number of characters to include in the substring (if the string is shorter, as many characters as possible are used).

len 要包含在子字符串中的字符数(如果字符串较短,则使用尽可能多的字符)。

testString has 10 chars, index positions 0 to 9. Specifying an endIndex of 10 should always throw the IndexOutOfBoundsException() because testString has no endIndex of 10.

testString 有 10 个字符,索引位置为 0 到 9。将 endIndex 指定为 10 应始终抛出 IndexOutOfBoundsException(),因为 testString 没有 endIndex 为 10。

If we test the method in JUnit with concrete values looking at the C++ method, we expect:

如果我们在 JUnit 中使用查看 C++ 方法的具体值测试该方法,我们期望:

String testString = "testString"; assertThat(testString.substring(4, 6), equalTo("String"));

String testString = "testString"; assertThat(testString.substring(4, 6), equalTo("String"));

but of course we get Expected: "String" but was "St"

但当然我们得到了预期的:“String”但是是“St”

The length of testString from index 0 to char 'g' in 'String' is 10 chars. If we use 10 as the 'endIndex' parameter,

testString 从索引 0 到 'String' 中的字符 'g' 的长度为 10 个字符。如果我们使用 10 作为 'endIndex' 参数,

String testString = "testString"; assertThat(testString.substring(4, 10), equalTo("String"));

String testString = "testString"; assertThat(testString.substring(4, 10), equalTo("String"));

"Pass" from JUnit.

来自 JUnit 的“通过”。

If we rename parameter 2 to "lengthOfSubstringFromIndex0" you don't have to do the endIndex - 1 count, and it never throws the IndexOutOfBoundsException() that is expected when specifying an endIndex, 10, that is out of range for the underlying array. http://docs.oracle.com/javase/7/docs/api/java/lang/IndexOutOfBoundsException.html

如果我们将参数 2 重命名为“lengthOfSubstringFromIndex0”,则您不必执行 endIndex - 1 计数,并且它永远不会抛出预期的 IndexOutOfBoundsException(),这是在指定超出基础数组范围的 endIndex 10 时所预期的。http://docs.oracle.com/javase/7/docs/api/java/lang/IndexOutOfBoundsException.html

This is just one of those times that you have to remember the idiosyncrasy of this method. The second parameter is not named correctly. The Java method signature should be:

这只是您必须记住这种方法的特性的时间之一。第二个参数没有正确命名。Java 方法签名应该是:

public String substring(int beginIndex,
           int lengthOfSubstringFromIndex0)

Or the method redefined to match C++ string::substr method. Redefining of course would mean rewriting the entire internet, so it's not likely.

或者重新定义方法以匹配 C++ string::substr 方法。重新定义当然意味着重写整个互联网,所以不太可能。