Java子字符串:'String index out of range'

33
我猜想这个错误是因为字符串正在尝试对null值进行子串处理。但是,".length() > 0"部分不应该解决这个问题吗?
下面是Java代码片段:
if (itemdescription.length() > 0) {
    pstmt2.setString(3, itemdescription.substring(0,38));
} else { 
    pstmt2.setString(3, "_");
} 

我遇到了这个错误:
 java.lang.StringIndexOutOfBoundsException: String index out of range: 38
    at java.lang.String.substring(Unknown Source)
    at MASInsert2.itemimport(MASInsert2.java:192)
    at MASInsert2.processRequest(MASInsert2.java:125)
    at MASInsert2.doGet(MASInsert2.java:219)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:627)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:269)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:172)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:174)
    at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:835)
    at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:640)
    at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1286)
    at java.lang.Thread.run(Unknown Source)
13个回答

71

很遗憾,substring 的实现方式不能处理短字符串 - 就像其他编程语言(例如 Python)一样。

好的,我们无法改变这个事实并且每次使用 substr 时都必须考虑到这种边缘情况,我会选择使用这种更短的变量而不是 if-else 条件语句:

myText.substring(0, Math.min(6, myText.length()))

10
好的,这太荒谬了。 - Josh M.

38
我猜我得到这个错误是因为该字符串试图从Null值中提取子字符串。但是,".length() > 0"的部分不会消除这个问题吗?
不,当itemdescription为null时调用itemdescription.length()不会生成StringIndexOutOfBoundsException,而是生成NullPointerException,因为您实际上是在尝试调用null上的方法。
正如其他人所指出的那样,StringIndexOutOfBoundsException表示itemdescription长度不足38个字符。您可能希望处理这两种情况(假设您想要截断)。
final String value;
if (itemdescription == null || itemdescription.length() <= 0) {
    value = "_";
} else if (itemdescription.length() <= 38) {
    value = itemdescription;
} else { 
    value = itemdescription.substring(0, 38);
}
pstmt2.setString(3, value);

如果你经常这样做的话,这可能是一个很好的实用函数的位置...


13

我建议使用Apache Commons Lang。一个一行代码就能解决这个问题。

pstmt2.setString(3, StringUtils.defaultIfEmpty(
    StringUtils.subString(itemdescription,0, 38), "_")); 

10

当你尝试从一个索引开始获取子字符串时,Java的substring方法会失败,如果该索引比字符串长度还要长。

一个简单的替代方法是使用Apache Commons StringUtils.substring

public static String substring(String str, int start)

Gets a substring from the specified String avoiding exceptions.

A negative start position can be used to start n characters from the end of the String.

A null String will return null. An empty ("") String will return "".

 StringUtils.substring(null, *)   = null
 StringUtils.substring("", *)     = ""
 StringUtils.substring("abc", 0)  = "abc"
 StringUtils.substring("abc", 2)  = "c"
 StringUtils.substring("abc", 4)  = ""
 StringUtils.substring("abc", -2) = "bc"
 StringUtils.substring("abc", -4) = "abc"

Parameters:
str - the String to get the substring from, may be null
start - the position to start from, negative means count back from the end of the String by this many characters

Returns:
substring from start position, null if null String input

注意,如果由于某些原因你无法使用Apache Commons库,你可以从源代码中获取所需部分


// Substring
//-----------------------------------------------------------------------
/**
 * <p>Gets a substring from the specified String avoiding exceptions.</p>
 *
 * <p>A negative start position can be used to start {@code n}
 * characters from the end of the String.</p>
 *
 * <p>A {@code null} String will return {@code null}.
 * An empty ("") String will return "".</p>
 *
 * <pre>
 * StringUtils.substring(null, *)   = null
 * StringUtils.substring("", *)     = ""
 * StringUtils.substring("abc", 0)  = "abc"
 * StringUtils.substring("abc", 2)  = "c"
 * StringUtils.substring("abc", 4)  = ""
 * StringUtils.substring("abc", -2) = "bc"
 * StringUtils.substring("abc", -4) = "abc"
 * </pre>
 *
 * @param str  the String to get the substring from, may be null
 * @param start  the position to start from, negative means
 *  count back from the end of the String by this many characters
 * @return substring from start position, {@code null} if null String input
 */
public static String substring(final String str, int start) {
    if (str == null) {
        return null;
    }

    // handle negatives, which means last n characters
    if (start < 0) {
        start = str.length() + start; // remember start is negative
    }

    if (start < 0) {
        start = 0;
    }
    if (start > str.length()) {
        return EMPTY;
    }

    return str.substring(start);
}

10

你需要检查字符串的长度是否大于或等于38。


6

substring(0,38) 的意思是字符串的长度必须是38个字符或更长。如果不是,会出现“String index is out of range”的错误。


5
if (itemdescription != null && itemdescription.length() > 0) {
    pstmt2.setString(3, itemdescription.substring(0, Math.min(itemdescription.length(), 38))); 
} else { 
    pstmt2.setString(3, "_"); 
}

2
我非常想知道itemdescription.substring(0, itemdescription.length())会返回什么 :) - pugmarx
变量 itemdescription = new String("Hello, World!"); alert( itemdescription.substring(0, itemdescription.length) );返回 "Hello, World!"。 - tom
他可能想用它做些什么。 - Tom Hawtin - tackline
嗯,是的,在那里应该有一个上限。这就是午夜发布的原因... - JeeBee

2

我假设你的列长度为38个字符,因此你想要截短itemdescription以适应数据库。下面这个实用函数应该可以满足你的需求:

/**
 * Truncates s to fit within len. If s is null, null is returned.
 **/
public String truncate(String s, int len) { 
  if (s == null) return null;
  return s.substring(0, Math.min(len, s.length()));
}

那么您只需要这样调用它:
String value = "_";
if (itemdescription != null && itemdescription.length() > 0) {
  value = truncate(itemdescription, 38);
}

pstmt2.setString(3, value);

2

itemdescription的长度小于38个字符,因此会抛出StringOutOfBoundsException异常。

检查.length() > 0只是确保String具有一些非空值,你需要做的是检查长度是否足够。你可以尝试:

if(itemdescription.length() > 38)
  ...

0
当适用时,我使用匹配(matches)而不是子字符串(substring)
使用子字符串(substring)
if( myString.substring(1,17).equals("Someting I expect") ) {
    // Do stuff
}
// Does NOT work if myString is too short

使用正则表达式进行匹配:

if( myString.matches("Someting I expect.*") ) {
    // Do stuff
}
// This works with all strings

1
使用 == 进行字符串比较在大多数情况下是无效的。模式匹配与字符串比较有很大的区别,在这种情况下,这是一个不好的想法,尤其是出于性能原因(但不仅限于此)。 - Alexandre Cartapanis
你说得对,亚历山大;改为equals。我发现正则表达式非常方便易用,但也确实不如其他方法高效。 - F. Emond

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接