Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Test boundary of String.Index for substring function

Tags:

string

swift

Wow. Swift makes it really fiddly to copy a substring from a simple String.

Most programming languages allow characters to be simply indexed by their integer position in the string, making targeting a character or a range a matter of simple maths. Because Swift allows a wide range of characters to be used with various bit depths, a precise (memory?) index for each character has to be found first, based its position from the start or end of the string. These positions can then be passed into a method of the String class that returns the substring in the range. I've written a function to do the work:

//arguments: The parent string, number of chars from 1st char in it and total char length of substring

func subStr(str: String, c1: Int, c2: Int) -> String {
    //get string indexes for range of substring based on args
    let ind1 = str.startIndex.advancedBy(c1)
    let ind2 = str.startIndex.advancedBy(c1+c2)
//calls substring function with a range object passed as an argument set to the index values
    let sub = str.substringWithRange(Range<String.Index>(start: ind1, end: ind2))
//substring returned
    return sub
}

The problem is that because the substringWithRange function only works with Range objects its not obvious how to check if the substring is out of bounds of the string. For example calling this function would produce an error:

subStr ("Stack Overflow", c1: 6, c2: 12)

The c1 argument is OK but the length of the (c2) substring exceeds the upper boundary of the parent string causing a fatal error.

How do I refine this function to make it handle bad arguments or otherwise refine this clunky process?

Many thanks for reading.

like image 759
Kwangle Avatar asked Nov 03 '15 19:11

Kwangle


2 Answers

You can use

let ind1 = str.startIndex.advancedBy(c1, limit: str.endIndex)
let ind2 = str.startIndex.advancedBy(c1+c2, limit: str.endIndex)

to advance the start index by the given amounts, but not beyond the end index of the string. With that modification, your function gives

subStr ("Stack Overflow", c1: 6, c2: 12) // "Overflow"
subStr ("Stack Overflow", c1: 12, c2: 20) // ""
like image 199
Martin R Avatar answered Oct 19 '22 19:10

Martin R


I quickly made a nice substring function for Strings without Foundation dependency:

extension String {
    func substring(from: Int, length: Int) -> String {
        return String(dropFirst(from).prefix(length))
    }
}

If the length is bigger than possible, it just gives all the characters to the end. Can't crash Can't crash as long as neither argument is negative (it makes sense to crash if any argument is negative since that would be a major flaw in your code).

Example usage:

"Stack Overflow".substring(from: 6, length: 12)

gives

"Overflow"
like image 22
Kametrixom Avatar answered Oct 19 '22 19:10

Kametrixom