Why Is "Out of Range" Not Thrown for 'Substring(Startindex, Endindex)'

Why is out of range not thrown for 'substring(startIndex, endIndex)'

According to the Java API doc, substring throws an error when the start index is greater than the Length of the String.

IndexOutOfBoundsException - if
beginIndex is negative or larger than
the length of this String object.

In fact, they give an example much like yours:

"emptiness".substring(9) returns "" (an empty string)

I guess this means it is best to think of a Java String as the following, where an index is wrapped in |:

|0| A |1| B |2| C |3| D |4| E |5|

Which is to say a string has both a start and end index.

Both start index and end index are out of range but the substring method still works in Java

Acording to the documentation of the method:

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

The length of your string is 1 and not 0, so it doens't throw and exception.

Java substring: 'string index out of range'

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...

System.ArgumentOutOfRangeException: Length cannot be less than zero

This is a frequent mistake in .NET when using the Substring method. People often think that the Substring takes the start and end index which is wrong. It takes the start index and the length. For example:

string str = "ABC123";

int length = str.Length - str.IndexOf("1") + 1;
string sub = str.Substring(0, length); // ABC1

Or better, create an extension method for this reusable piece to add the Java Like Substring in C# that takes a start and end index:

public static class MyExtensions
{
public static string SubstringRange(this string str, int startIndex, int endIndex)
{
if (startIndex > str.Length - 1)
throw new ArgumentOutOfRangeException(nameof(startIndex));
if (endIndex > str.Length - 1)
throw new ArgumentOutOfRangeException(nameof(endIndex));

return str.Substring(startIndex, endIndex - startIndex + 1);
}
}

Usage:

string str = "ABC123";

string sub2 = str.SubstringRange(str.IndexOf("B"), str.IndexOf("2")); // BC12

Substring strange Bounds?

Okay people have been throwing java doc around but that doesn't really answer the question.

The reason you can do "D".substring(1) is because you're asking for the string starting at index 1, to index 1 (exclusive). This is by definition the empty string as its size is 1-1=0. Every string has an empty string at the start, end and between every character. E.g. "" + "B" + "O" + "" + "B" + "" = "BOB".

Why java substring doesn't crash?

Per http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#substring(int)

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

here, your string is of length 1, your index is 1 and it is not negative => no exception thrown.

A little unintuitive? Yes. But substring() is a little unintuitive with its index choices in general :-)

Cannot invoke initializer for type 'RangeString.Index' with an argument list of type '(RangeString.Index)'

Some background:

In Swift 3, additional range types were introduced, making a total
of four (see for example Ole Begemann: Ranges in Swift 3):

Range, ClosedRange, CountableRange, CountableClosedRange

With the implementation of SE-0143 Conditional conformances in Swift 4.2, the “countable” variants
are not separate types anymore, but (constrained) type aliases, for example

 public typealias CountableRange<Bound: Strideable> = Range<Bound>
where Bound.Stride : SignedInteger

and, as a consequence, various conversions between the different
range types have been removed, such as the

init(_ other: Range<Range.Bound>)

initializer of struct Range. All theses changes are part of the
[stdlib][WIP] Eliminate (Closed)CountableRange using conditional conformance (#13342) commit.

So that is the reason why

let range: Range<Index> = Range<Index>(start..<self.endIndex)

does not compile anymore.

How to fix

As you already figured out, this can be simply fixed as

let range: Range<Index> = start..<self.endIndex

or just

let range = start..<self.endIndex

without the type annotation.

Another option is to use a one sided range
(introduced in Swift 4 with SE-0172 One-sided Ranges):

extension String {
func index(of aString: String, startingFrom position: Int = 0) -> String.Index? {
let start = index(startIndex, offsetBy: position)
return self[start...].range(of: aString, options: .literal)?.lowerBound
}
}

This works because the substring self[start...] shares its indices
with the originating string self.



Related Topics



Leave a reply



Submit