在substring / indexOf期间接收StringIndexOutOfBoundsException

时间:2016-06-24 22:55:17

标签: java string substring indexoutofboundsexception 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,打印字符串.中的文字。

3 个答案:

答案 0 :(得分:0)

您尝试查找.的索引时出错:

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);
    }
}

答案 1 :(得分:0)

它接收你的子串并找到它,然后再次读取文件并找到第一个期间。所以,什么时候

此。一些东西。乔治布什是等等等等。

它将你的第一个参数作为一个比第二个更大的数字,因为它在&#34之后找到第一个时期;这个"。

如果你想继续这样做,你必须在找到&#34之后截断字符串;乔治布什是"把它放在字符串的开头。

答案 2 :(得分:0)

最小,完整且可验证的示例

为了帮助我们,您应该提供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)失败。

这是因为periodIdxsubstring的索引,而不是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

解决方案2(不理想)

修复它的另一种方法是改为对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

解决方案3(理想)

以前的两种解决方案都能为您提供所需的结果。但它们不是理想的解决方案,因为content.substring(searchIdx)在创建子字符串时会使用副本

更好的解决方案是在第一次查找返回的点执行第二次索引启动

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万(!)字符两次

它还使代码更具可读性。