33

I guess I'm getting this error because the string is trying to substring a null value. But wouldn't the ".length() > 0" part eliminate that issue?

Here is the Java snippet:

if (itemdescription.length() > 0) {
    pstmt2.setString(3, itemdescription.substring(0,38));
} else { 
    pstmt2.setString(3, "_");
} 

I got this error:

 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 Answers 13

74

It is a pity that substring is not implemented in a way that handles short strings – like in other languages e.g. Python.

Ok, we cannot change that and have to consider this edge case every time we use substr, instead of if-else clauses I would go for this shorter variant:

myText.substring(0, Math.min(6, myText.length()))
Sign up to request clarification or add additional context in comments.

1 Comment

Yes, this is so ridiculous.
38

I"m guessing i'm getting this error because the string is trying to substring a Null value. But wouldn't the ".length() > 0" part eliminate that issue?

No, calling itemdescription.length() when itemdescription is null would not generate a StringIndexOutOfBoundsException, but rather a NullPointerException since you would essentially be trying to call a method on null.

As others have indicated, StringIndexOutOfBoundsException indicates that itemdescription is not at least 38 characters long. You probably want to handle both conditions (I assuming you want to truncate):

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

Might be a good place for a utility function if you do that a lot...

Comments

13

I would recommend apache commons lang. A one-liner takes care of the problem.

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

Comments

12

Java's substring method fails when you try and get a substring starting at an index which is longer than the string.

An easy alternative is to use 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

Note, if you can't use Apache Commons lib for some reason, you could just grab the parts you need from the source

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

Comments

10

You really need to check if the string's length is greater to or equal to 38.

Comments

6

substring(0,38) means the String has to be 38 characters or longer. If not, the "String index is out of range".

Comments

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

4 Comments

I'd be really interested in knowing what itemdescription.substring(0, itemdescription.length()) would return :)
var itemdescription = new String("Hello, World!"); alert( itemdescription.substring(0, itemdescription.length) ); returns "Hello, World!".
He probably wanted to do something with it.
hmm, yes, should have had an upper bound there. that's midnight posting for you...
2

I'm assuming your column is 38 characters in length, so you want to truncate itemdescription to fit within the database. A utility function like the following should do what you want:

/**
 * 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()));
}

then you just call it like so:

String value = "_";
if (itemdescription != null && itemdescription.length() > 0) {
  value = truncate(itemdescription, 38);
}

pstmt2.setString(3, value);

Comments

2

itemdescription is shorter than 38 chars. Which is why the StringOutOfBoundsException is being thrown.

Checking .length() > 0 simply makes sure the String has some not-null value, what you need to do is check that the length is long enough. You could try:

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

Comments

0

You must check the String length. You assume that you can do substring(0,38) as long as String is not null, but you actually need the String to be of at least 38 characters length.

Comments

0

When this is appropriate, I use matches instead of substring.

With substring:

if( myString.substring(1,17).equals("Someting I expect") ) {
    // Do stuff
}
// Does NOT work if myString is too short

With matches (must use Regex notation):

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

2 Comments

Comparing strings with == will not work in most of case. Pattern matching is quite different from string comparison, and in this case it is a bad idea, notably (but not only) for performance reason.
You're right Alexandre; changed for equals. I find Regex very handy and easy to use, but it's right also that it is less performant.
0

Should anyone face the same problem.

Do this: str.substring (...(trim()) ;

Hope it helps somebodies 😎

Comments

-1

You get this if itemdescription is shorter than 38 characters

You can look which exceptions are thrown and when in the JAVA API in you case for String#substring(int,int): https://docs.oracle.com/javase/9/docs/api/java/lang/String.html#substring-int-int-

substring
public String substring(int beginIndex, int endIndex)
   . . .

Throws:
 IndexOutOfBoundsException
 if the beginIndex is negative,
 or endIndex is larger than the length of this String object, 
 or beginIndex is larger than endIndex.



(same applies to previous java versions as well)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.