为啥“子字符串(startIndex,endIndex)”没有抛出“超出范围”
Posted
技术标签:
【中文标题】为啥“子字符串(startIndex,endIndex)”没有抛出“超出范围”【英文标题】:Why is "out of range" not thrown for 'substring(startIndex, endIndex)'为什么“子字符串(startIndex,endIndex)”没有抛出“超出范围” 【发布时间】:2011-03-15 02:04:01 【问题描述】:在 Java 中,我使用的是 substring()
方法,但我不确定它为什么没有抛出“超出索引”错误。
字符串 abcde
的索引从 0 到 4 开始,但 substring()
方法将 startIndex 和 endIndex 作为参数,因为我可以调用 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
【问题讨论】:
【参考方案1】:substring(5) 指向一个现有的索引...它恰好指向一个空字符串。另一方面,substring(6) 只是疯狂的谈话。 :)
【讨论】:
【参考方案2】:当您执行foo.substring(5)
时,它会获取从“e”之后的位置开始并在字符串末尾结束的子字符串。顺便说一句,开始和结束位置恰好是相同的。因此,空字符串。您可以认为索引不是字符串中的实际字符,而是字符之间的位置。
---------------------
String: | a | b | c | d | e |
---------------------
Index: 0 1 2 3 4 5
【讨论】:
【参考方案3】:根据Java API doc,当起始索引大于字符串的长度时,子字符串会抛出错误。
IndexOutOfBoundsException - 如果 beginIndex 为负数或大于 此 String 对象的长度。
事实上,他们举了一个很像你的例子:
"emptiness".substring(9) returns "" (an empty string)
我想这意味着最好将 Java 字符串视为以下内容,其中索引包含在 |
中:
|0| A |1| B |2| C |3| D |4| E |5|
也就是说一个字符串既有开始索引又有结束索引。
【讨论】:
啊!感谢您的提示,我正在查看相同的文档页面,但不知道我必须一直向下滚动才能获得更多详细信息... 希望 javadoc 可以对此进行注释,否则像我这样粗心的人会期望IndexOutOfBoundsException
如果 beginIndex=String.length() 发生。【参考方案4】:
这是因为 substring 函数返回一个“包含”的子字符串。所以索引 5 指向字符串末尾之前的位置,但在字符串的最后一个显示字符之后。
这显示在文档中: http://download.oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/api/java/lang/String.html#substring(int)
【讨论】:
【参考方案5】:来自字符串 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.
====
所以这是设计使然。如果将索引作为字符串的大小,则返回空字符串。
【讨论】:
【参考方案6】:我知道这个帖子已经很老了,但这是一个非常基本的问题,我认为它值得澄清。
问题是正确的。我认为这是 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/C/C++ 和我所知道的所有其他语言都不会将数组索引视为数组元素之间的“分隔符”。
参数: beginIndex - 起始索引,包括在内。 endIndex - 结束索引,不包含。
endIndex 名称错误,因为该语言不允许内存访问 endIndex + 1 处的地址,该地址需要包含最后一个数组元素,或者 endIndex 定义错误并且必须是: endIndex - 结束索引,包括。
最有可能的情况是第二个参数命名错误。它应该是: length - 从 beginIndex 开始的所需字符串的长度。
我们知道 Gosling 基于 C/C++ 语言的 Java 语法是为了熟悉。从C++字符串类http://www.cplusplus.com/reference/string/string/substr/我们看到方法定义是:
string substr (size_t pos = 0, size_t len = npos) const;
请注意,方法定义中的第二个参数是 'len' 表示长度。
长度 要包含在子字符串中的字符数(如果字符串较短,则使用尽可能多的字符)。
testString 有 10 个字符,索引位置为 0 到 9。指定 10 的 endIndex 应该总是抛出 IndexOutOfBoundsException(),因为 testString 没有 10 的 endIndex。
如果我们用 C++ 方法的具体值测试 JUnit 中的方法,我们期望:
字符串 testString = "testString"; assertThat(testString.substring(4, 6), equalTo("String"));
但我们当然会得到 Expected: "String" but was "St"
'String' 中从索引 0 到字符 'g' 的 testString 长度为 10 个字符。 如果我们使用 10 作为 'endIndex' 参数,
字符串 testString = "testString"; assertThat(testString.substring(4, 10), equalTo("String"));
来自 JUnit 的“通过”。
如果我们将参数 2 重命名为“lengthOfSubstringFromIndex0”,则您不必进行 endIndex - 1 计数,并且它永远不会抛出 IndexOutOfBoundsException(),而在指定 endIndex 10 时,它超出了底层数组。 http://docs.oracle.com/javase/7/docs/api/java/lang/IndexOutOfBoundsException.html
这只是您必须记住这种方法的特殊性的那些时候之一。第二个参数命名不正确。 Java 方法签名应该是:
public String substring(int beginIndex,
int lengthOfSubstringFromIndex0)
或重新定义方法以匹配 C++ string::substr 方法。重新定义当然意味着重写整个互联网,所以不太可能。
【讨论】:
以上是关于为啥“子字符串(startIndex,endIndex)”没有抛出“超出范围”的主要内容,如果未能解决你的问题,请参考以下文章
为啥 DataGridView 上的 DoubleBuffered 属性默认为 false,为啥它受到保护?