在 substring / indexOf 期间接收 StringIndexOutOfBoundsException
Receiving StringIndexOutOfBoundsException during substring / indexOf
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class Untitled {
public static void main(String[] args) throws IOException {
String content = new String(Files.readAllBytes(Paths.get("YUGECORPUS.txt")));
content = content.replace("\n", " ").replace("\r", " ");
String search = "George Bush is";
System.out.print(content.substring(content.indexOf(search), content.substring(content.indexOf(search)).indexOf(".")));
}
}
我在编译代码时收到的错误如下:
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -3073945
at java.lang.String.substring(String.java:1967)
at Untitled.main(main.java:14)
.
如何修复此错误,为什么会出现此错误?上面代码段中以下代码的用途:
content.substring(content.indexOf(search), content.substring(content.indexOf(search)).indexOf("."))
打印字符串content
从第一次出现的search
开始到第一次出现句点.
的文本。
您尝试查找 .
的索引时出错:
content.substring(content.indexOf(search)).indexOf(".")
将为您提供子字符串内的索引,而不是 content
内的索引。要修复,您应该将 search
的起始索引添加到它。
例如,如果 content
是:123George Bush is45.
那么我们将有:
content.indexOf(search) -> 3
content.substring(content.indexOf(search)) -> "George Bush is45."
因此:
content.substring(content.indexOf(search)).indexOf(".") -> 16
这是不正确的,正确的索引是 16 + 3 = 19:
content.substring(3, 16) -> "George Bush i" // wrong
content.substring(3, 19) -> "George Bush is45" // correct
此外,如果 content
不包含您的 search
字符串和连续的 .
您的代码也可能无法正常工作并产生异常。
为了使代码更加防错,您可以添加检查 content
是否包含您期望的内容,就像这样(注意 endIndex += startIndex
处的修复):
int startIndex = content.indexOf(search);
if(startIndex > -1) {
int endIndex = content.substring(startIndex).indexOf(".");
if(endIndex > -1) {
endIndex += startIndex;
String foundString = content.substring(startIndex, endIndex);
System.out.print(foundString);
}
}
明确地进行检查而不是将所有内容都放在一行中也会使代码更容易调试和发现错误。
编辑: 正如@Andreas 所指出的,说明
int endIndex = content.substring(startIndex).indexOf(".");
endIndex += startIndex;
可以简化为
int endIndex = content.indexOf('.', startIndex);
这是更新后的代码:
int startIndex = content.indexOf(search);
if(startIndex > -1) {
int endIndex = content.indexOf('.', startIndex);
if(endIndex > -1) {
String foundString = content.substring(startIndex, endIndex);
System.out.print(foundString);
}
}
它获取您的子字符串并找到它,然后再次读取文件并找到 第一个 句点。所以,当
这个。某物。乔治·布什 (George Bush) 等等等等。
它得到的第一个参数比第二个参数大,因为它在 "This" 之后找到第一个句点。
如果您想继续这样做,您必须在找到 "George bush is" 后截断字符串以将其放在字符串的开头。
最小、完整且可验证的示例
为了帮助我们帮助您,您应该提供 MCVE。这很容易通过替换方法中的第一行来完成,例如
String content = "In a galaxy far, far away, George Bush is happy. That is good.";
那样的话,我们实际上可以重现您的问题。
问题
所以,完成后,让我们拆分您的代码,看看出了什么问题:
String content = "In a galaxy far, far away, George Bush is happy. That is good.";
content = content.replace("\n", " ").replace("\r", " ");
String search = "George Bush is";
int searchIdx = content.indexOf(search);
String substring = content.substring(searchIdx);
int periodIdx = substring.indexOf(".");
System.out.println("searchIdx = " + searchIdx);
System.out.println("substring = " + substring);
System.out.println("periodIdx = " + periodIdx);
System.out.print("content.substring(" + searchIdx + ", " + periodIdx + ") = ");
System.out.flush();
System.out.println(content.substring(searchIdx, periodIdx));
输出
searchIdx = 27
substring = George Bush is happy. That is good.
periodIdx = 20
content.substring(27, 20) = Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -7
at java.lang.String.substring(String.java:1967)
at Test.main(Test.java:18)
这里可以看到问题是periodIdx
为20,即小于searchIdx
的值27,导致substring(27, 20)
失败
这是因为 periodIdx
是 substring
的索引,而不是 content
.
的索引
解决方案 1(不理想)
解决这个问题的一种方法是简单地将 searchIdx
添加到 periodIdx
,例如
int periodIdx = substring.indexOf(".") + searchIdx;
输出
searchIdx = 27
substring = George Bush is happy. That is good.
periodIdx = 47
content.substring(27, 47) = George Bush is happy
方案二(不理想)
另一种解决方法是将 substring
变量改为子字符串,因为这是索引的用途:
int periodIdx = substring.indexOf(".");
System.out.print("substring.substring(0, " + periodIdx + ") = ");
System.out.println(substring.substring(0, periodIdx));
输出
substring.substring(0, 20) = George Bush is happy
方案三(理想)
前面的两种解决方案都会让您得到想要的结果。然而,它们不是 理想的 解决方案,因为 content.substring(searchIdx)
在创建子字符串时需要 copy。
更好的解决方案是在第一次查找返回的点开始执行第二次索引查找:
int searchIdx = content.indexOf(search);
int periodIdx = content.indexOf('.', searchIdx);
System.out.print("content.substring(" + searchIdx + ", " + periodIdx + ") = ");
System.out.println(content.substring(searchIdx, periodIdx));
输出
content.substring(27, 47) = George Bush is happy
另请注意,indexOf()
的搜索值已从 "."
更改为 '.'
,因为搜索单个字符比搜索字符串更快,甚至是单字符字符串。
这是更好的代码。
结论
您的 main()
方法应该是:
public static void main(String[] args) throws IOException {
String content = new String(Files.readAllBytes(Paths.get("YUGECORPUS.txt")));
content = content.replace("\n", " ").replace("\r", " ");
String search = "George Bush is";
int searchIdx = content.indexOf(search);
System.out.print(content.substring(searchIdx, content.indexOf('.', searchIdx)));
}
请注意,searchIdx
是单独完成的,因此只需执行一次,这与您的代码不同,后者必须搜索超过 超过 300 万(!) 个字符两次.
这也使代码更具可读性。
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class Untitled {
public static void main(String[] args) throws IOException {
String content = new String(Files.readAllBytes(Paths.get("YUGECORPUS.txt")));
content = content.replace("\n", " ").replace("\r", " ");
String search = "George Bush is";
System.out.print(content.substring(content.indexOf(search), content.substring(content.indexOf(search)).indexOf(".")));
}
}
我在编译代码时收到的错误如下:
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -3073945
at java.lang.String.substring(String.java:1967)
at Untitled.main(main.java:14)
.
如何修复此错误,为什么会出现此错误?上面代码段中以下代码的用途:
content.substring(content.indexOf(search), content.substring(content.indexOf(search)).indexOf("."))
打印字符串content
从第一次出现的search
开始到第一次出现句点.
的文本。
您尝试查找 .
的索引时出错:
content.substring(content.indexOf(search)).indexOf(".")
将为您提供子字符串内的索引,而不是 content
内的索引。要修复,您应该将 search
的起始索引添加到它。
例如,如果 content
是:123George Bush is45.
那么我们将有:
content.indexOf(search) -> 3
content.substring(content.indexOf(search)) -> "George Bush is45."
因此:
content.substring(content.indexOf(search)).indexOf(".") -> 16
这是不正确的,正确的索引是 16 + 3 = 19:
content.substring(3, 16) -> "George Bush i" // wrong
content.substring(3, 19) -> "George Bush is45" // correct
此外,如果 content
不包含您的 search
字符串和连续的 .
您的代码也可能无法正常工作并产生异常。
为了使代码更加防错,您可以添加检查 content
是否包含您期望的内容,就像这样(注意 endIndex += startIndex
处的修复):
int startIndex = content.indexOf(search);
if(startIndex > -1) {
int endIndex = content.substring(startIndex).indexOf(".");
if(endIndex > -1) {
endIndex += startIndex;
String foundString = content.substring(startIndex, endIndex);
System.out.print(foundString);
}
}
明确地进行检查而不是将所有内容都放在一行中也会使代码更容易调试和发现错误。
编辑: 正如@Andreas 所指出的,说明
int endIndex = content.substring(startIndex).indexOf(".");
endIndex += startIndex;
可以简化为
int endIndex = content.indexOf('.', startIndex);
这是更新后的代码:
int startIndex = content.indexOf(search);
if(startIndex > -1) {
int endIndex = content.indexOf('.', startIndex);
if(endIndex > -1) {
String foundString = content.substring(startIndex, endIndex);
System.out.print(foundString);
}
}
它获取您的子字符串并找到它,然后再次读取文件并找到 第一个 句点。所以,当
这个。某物。乔治·布什 (George Bush) 等等等等。
它得到的第一个参数比第二个参数大,因为它在 "This" 之后找到第一个句点。
如果您想继续这样做,您必须在找到 "George bush is" 后截断字符串以将其放在字符串的开头。
最小、完整且可验证的示例
为了帮助我们帮助您,您应该提供 MCVE。这很容易通过替换方法中的第一行来完成,例如
String content = "In a galaxy far, far away, George Bush is happy. That is good.";
那样的话,我们实际上可以重现您的问题。
问题
所以,完成后,让我们拆分您的代码,看看出了什么问题:
String content = "In a galaxy far, far away, George Bush is happy. That is good.";
content = content.replace("\n", " ").replace("\r", " ");
String search = "George Bush is";
int searchIdx = content.indexOf(search);
String substring = content.substring(searchIdx);
int periodIdx = substring.indexOf(".");
System.out.println("searchIdx = " + searchIdx);
System.out.println("substring = " + substring);
System.out.println("periodIdx = " + periodIdx);
System.out.print("content.substring(" + searchIdx + ", " + periodIdx + ") = ");
System.out.flush();
System.out.println(content.substring(searchIdx, periodIdx));
输出
searchIdx = 27
substring = George Bush is happy. That is good.
periodIdx = 20
content.substring(27, 20) = Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -7
at java.lang.String.substring(String.java:1967)
at Test.main(Test.java:18)
这里可以看到问题是periodIdx
为20,即小于searchIdx
的值27,导致substring(27, 20)
失败
这是因为 periodIdx
是 substring
的索引,而不是 content
.
解决方案 1(不理想)
解决这个问题的一种方法是简单地将 searchIdx
添加到 periodIdx
,例如
int periodIdx = substring.indexOf(".") + searchIdx;
输出
searchIdx = 27
substring = George Bush is happy. That is good.
periodIdx = 47
content.substring(27, 47) = George Bush is happy
方案二(不理想)
另一种解决方法是将 substring
变量改为子字符串,因为这是索引的用途:
int periodIdx = substring.indexOf(".");
System.out.print("substring.substring(0, " + periodIdx + ") = ");
System.out.println(substring.substring(0, periodIdx));
输出
substring.substring(0, 20) = George Bush is happy
方案三(理想)
前面的两种解决方案都会让您得到想要的结果。然而,它们不是 理想的 解决方案,因为 content.substring(searchIdx)
在创建子字符串时需要 copy。
更好的解决方案是在第一次查找返回的点开始执行第二次索引查找:
int searchIdx = content.indexOf(search);
int periodIdx = content.indexOf('.', searchIdx);
System.out.print("content.substring(" + searchIdx + ", " + periodIdx + ") = ");
System.out.println(content.substring(searchIdx, periodIdx));
输出
content.substring(27, 47) = George Bush is happy
另请注意,indexOf()
的搜索值已从 "."
更改为 '.'
,因为搜索单个字符比搜索字符串更快,甚至是单字符字符串。
这是更好的代码。
结论
您的 main()
方法应该是:
public static void main(String[] args) throws IOException {
String content = new String(Files.readAllBytes(Paths.get("YUGECORPUS.txt")));
content = content.replace("\n", " ").replace("\r", " ");
String search = "George Bush is";
int searchIdx = content.indexOf(search);
System.out.print(content.substring(searchIdx, content.indexOf('.', searchIdx)));
}
请注意,searchIdx
是单独完成的,因此只需执行一次,这与您的代码不同,后者必须搜索超过 超过 300 万(!) 个字符两次.
这也使代码更具可读性。