Cannot Increment Beyond Endindex

Cannot increment beyond endIndex

You can write something like this

extension String {
func substring(from offset: Int) -> String {
let fromIndex = index(self.startIndex, offsetBy: offset)
return substring(from: fromIndex)
}
}

Examples

"Hello world".substring(from: 0) // "Hello world"
"Hello world".substring(from: 1) // "ello world"
"Hello world".substring(from: 2) // "llo world"

What does happen if you pass the wrong param?

Something like this will generate a fatal error.

"Hello world".substring(from: 12)
fatal error: cannot increment beyond endIndex

You can make you code safer adding a guard statement like this

extension String {
func substring(from: Int) -> String? {
guard from < self.characters.count else { return nil }
let fromIndex = index(self.startIndex, offsetBy: from)
return substring(from: fromIndex)
}
}

Spaces in string causing fatal error: cannot increment beyond endIndex

Your code is working with the given string:

var str =  "PIXEL STUDIOS - TEST1"
let c = str.characters
let r = c.index(c.startIndex, offsetBy: 6)..<c.index(c.endIndex, offsetBy: 0)
let substring = str[r]
print(substring)

prints:

STUDIOS - TEST1

Conclusion: label.stringValue is fishy.

Print it out for further investigations.

String.endIndex incrementing error

There are two problems:

  • fieldName.endIndex is the "one past the end" position of the string,
    it has no successor.
  • You must not use the index of one string as the subscript for
    a different string. That may work in some cases, but can
    crash with a runtime exception if the strings contain
    characters outside of the "basic multilingual plane" (Emojis, flags, ...).

A working variant would be (Swift 2.2):

let fieldName = "VERSION:"
let versionField = "VERSION:4.1"

if versionField.hasPrefix(fieldName) {
let version = versionField.substringFromIndex(
versionField.startIndex.advancedBy(fieldName.characters.count))
print(version) // 4.1
} else {
print("No version found")
}

or alternatively:

if let range = versionField.rangeOfString(fieldName)
where range.startIndex == versionField.startIndex {
let version = versionField.substringFromIndex(range.endIndex)
print(version) // 4.1
} else {
print("No version found")
}

You can remove the constraint

where range.startIndex == versionField.startIndex

if the field should be found anywhere in the string.

Swift 3:

if versionField.hasPrefix(fieldName) {
let version = versionField.substring(
from: versionField.index(versionField.startIndex, offsetBy: fieldName.characters.count))
print(version) // 4.1
} else {
print("No version found")
}

or alternatively,

if let range = versionField.range(of: fieldName),
range.lowerBound == versionField.startIndex {
let version = versionField.substring(from: range.upperBound)
print(version) // 4.1
} else {
print("No version found")
}

Can't advance past endIndex Swift

You are modifying the string you are iterating over, so your indices become invalid. Instead, you could add a skipChar boolean that says that you've already handled the next character and then skip that character by executing continue:

func romanToInt(_ s: String) -> Int {
let romanDigits = ["I" : 1,
"V" : 5,
"X" : 10,
"L" : 50,
"C" : 100,
"D" : 500,
"M" : 1000]
let romanSums = ["IV" : 4,
"IX" : 9,
"XL" : 40,
"XC" : 90,
"CD" : 400,
"CM" : 900]
var sum = 0
var str = s
var charIndex = str.startIndex
var skipChar = false

for index in str.indices {
if skipChar {
skipChar = false
continue
}
if index != str.index(before: str.endIndex) {
charIndex = str.index(after: index)
} else {
charIndex = str.index(before: str.endIndex)
}
let chars = String(str[index]) + String(str[charIndex])
if romanSums[chars] != nil {
print(chars)
skipChar = true
sum += romanSums[chars]!
print(sum)
} else {
let char = String(str[index])
print(char)
sum += romanDigits[char]!
print(sum)
}
print(str)
}

return sum
}

let check = romanToInt("MCMXCIV")
print(check)
1994

Conforming String.CharacterView.Index to Strideable: fatal error when using stride(to:by:): cannot increment endIndex

Simply declaring the protocol conformance

extension String.CharacterView.Index : Strideable { }

compiles because String.CharacterView.Index conforms to
BidirectionalIndexType , and ForwardIndexType/BidirectionalIndexType have default method implementations for advancedBy() and distanceTo()
as required by Strideable.

Strideable has the default protocol method implementation
for stride():

extension Strideable {
// ...
public func stride(to end: Self, by stride: Self.Stride) -> StrideTo<Self>
}

So the only methods which are "directly" implemented for
String.CharacterView.Index are – as far as I can see - the successor() and predecessor() methods from BidirectionalIndexType.

As you already figured out, the default method implementation of
stride() does not work well with String.CharacterView.Index.

But is is always possible to define dedicated methods for a concrete type. For the problems of making String.CharacterView.Index conform to Strideable see
Vatsal Manot's answer below and the discussion in the comments – it took me a while to get what he meant :)

Here is a possible implementation of a stride(to:by:) method for String.CharacterView.Index:

extension String.CharacterView.Index {
typealias Index = String.CharacterView.Index

func stride(to end: Index, by stride: Int) -> AnySequence<Index> {

precondition(stride != 0, "stride size must not be zero")

return AnySequence { () -> AnyGenerator<Index> in
var current = self
return AnyGenerator {
if stride > 0 ? current >= end : current <= end {
return nil
}
defer {
current = current.advancedBy(stride, limit: end)
}
return current
}
}
}
}

This seems to work as expected:

let str = "01234"
str.startIndex.stride(to: str.endIndex, by: 2).forEach {
print($0,str.characters[$0])
}

Output

0 0
2 2
4 4

Swift 4 Substring Crash

If you use the variation with limitedBy parameter, that will return an optional value:

if let start = str.index(str.startIndex, offsetBy: 7, limitedBy: str.endIndex) {
...
}

That will gracefully detect whether the offset moves the index past the endIndex. Obviously, handle this optional however best in your scenario (if let, guard let, nil coalescing operator, etc.).

Different behavior of index(after:) in Collection and String

If we go to the source code of Array, we can find:

public func index(after i: Int) -> Int {
// NOTE: this is a manual specialization of index movement for a Strideable
// index that is required for Array performance. The optimizer is not
// capable of creating partial specializations yet.
// NOTE: Range checks are not performed here, because it is done later by
// the subscript function.
return i + 1
}

In this case, we can rewrite code like this and finally get the crash:

let a = [1]
let index = a.index(after: a.endIndex)
print(a[index])

So, all works "as expected" for Array type, but we must check the result index by yourself, if we don't want to get crash at runtime

P.S. Useful link by @MartinR: https://forums.swift.org/t/behaviour-of-collection-index-limitedby/19083/3

How to offset a RangeT in Swift?

There’s a second version of advance that takes a maximum index not to go beyond:

let s = "Hello, I must be going."
let range = s.startIndex..<s.endIndex

let startIndex = advance(range.startIndex, 50, s.endIndex)
let endIndex = advance(range.endIndex, 50, s.endIndex)
var newRange = startIndex..<endIndex

if newRange.isEmpty {
println("new range out of bounds")
}

Swift find all occurrences of a substring

You just keep advancing the search range until you can't find any more instances of the substring:

extension String {
func indicesOf(string: String) -> [Int] {
var indices = [Int]()
var searchStartIndex = self.startIndex

while searchStartIndex < self.endIndex,
let range = self.range(of: string, range: searchStartIndex..<self.endIndex),
!range.isEmpty
{
let index = distance(from: self.startIndex, to: range.lowerBound)
indices.append(index)
searchStartIndex = range.upperBound
}

return indices
}
}

let keyword = "a"
let html = "aaaa"
let indicies = html.indicesOf(string: keyword)
print(indicies) // [0, 1, 2, 3]

Insert Charc into String in a Range where character X exist at Index - swift 2.0

The following finds the first space in the range 15..<END_OF_YOUR_STRING, and replaces it with a new line (\n). In your question you stated you explicitly wanted to look for a space in range 15...20, and also insert a new line after the space. Below I have assumed that you actually want:

  1. To replace the space by a new line, since you'll otherwise have a trailing space on the line following the line break.
  2. To search for the first space starting at index 15, but continuing until you find one (otherwise: if you find no space within range 15...20, no line break should be inserted?).

Both of these deviations from your question can be quite easily reverted, so tell me if you'd prefer me to follow your instructions to specifically to the point (rather than including my own reason), and I'll update this answer.

Solution as follows:

var foo = "This is my somewhat long test string"
let bar = 15 /* find first space " " starting from index 'bar' */
if let replaceAtIndex = foo[foo.startIndex.advancedBy(bar)..<foo.endIndex]
.rangeOfString(" ")?.startIndex.advancedBy(bar) {

foo = foo.stringByReplacingCharactersInRange(
replaceAtIndex...replaceAtIndex, withString: "\n")
}

print(foo)
/* This is my somewhat
long test string */

Note that there is a off-by-one difference between finding a space in the range of 15 to 20 and the 15:th to 20:th character (the latter is in the range 14...19). Above, we search for the first space starting at the 16th character (index 15).



Related Topics



Leave a reply



Submit