24

I would like to get the start and end position of a substring within a string. Example: in the string "hi this is my name"; if I provide the string "this" I would like to know the start index is 4 and end index is 7.

I found several links, including this one:

Swift: get index of start of a substring in a string a-substring-in-a-string

But it doesn´t work in swift 3 as the method is now called range.

I am now using this:

let range = mystring.range(of: "StringSearch")?.lowerBound

which returns this

Swift.String.UnicodeScalarView.Index(_position: 15), _countUTF16: 1))

And I cannot obtain the position as an integer as this is an index.

In summary, I would like to have in a variable of type int the position, in this case 15.

Can anyone help me?

Thanks everyone.

Community
  • 1
  • 1
rog
  • 363
  • 1
  • 2
  • 6
  • In what unit do you want that indexes, Characters, UnicodeScalars or UTF-16 code units? – OOPer Oct 16 '16 at 13:13
  • All relevant methods of `String` to split strings into substrings expect this index type. If you need the `Int` index to use the `NSString` methods. get rid of `NSString` – vadian Oct 16 '16 at 13:31
  • Hi. I would like to have the position (above 15) in a var of type int so I can use in another method. I needed something like lowerbound.position – rog Oct 16 '16 at 16:55

2 Answers2

30

The distance(from:to:) method of String computes the difference between two String.Index values:

let mystring = "hi this is my name"
if let range = mystring.range(of: "this") {
    let startPos = mystring.distance(from: mystring.startIndex, to: range.lowerBound)
    let endPos = mystring.distance(from: mystring.startIndex, to: range.upperBound)
    print(startPos, endPos) // 3 7
}

Actually it just forwards the call to the string's CharacterView, so the above gives the same result as

let mystring = "hi this is my name"
if let range = mystring.range(of: "this") {
    let startPos = mystring.characters.distance(from: mystring.characters.startIndex, to: range.lowerBound)
    let endPos = mystring.characters.distance(from: mystring.characters.startIndex, to: range.upperBound)
    print(startPos, endPos) // 3 7
}

If you need all occurrences of the string:

let mystring = "this is this and that is that"
var searchPosition = mystring.startIndex
while let range = mystring.range(of: "this", range: searchPosition..<mystring.endIndex) {
    let startPos = mystring.distance(from: mystring.startIndex, to: range.lowerBound)
    let endPos = mystring.distance(from: mystring.startIndex, to: range.upperBound)
    print(startPos, endPos)

    searchPosition = range.upperBound
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
3

Adapted answer from Martin R into a function gives you the first occurrence as an NSRange. You could turn this into an extension of String also.

public class func indexOf(string: String, substring: String) -> NSRange? {
    if let range = string.range(of: substring) {
        let startPos = string.distance(from: string.startIndex, to: range.lowerBound)
        return NSMakeRange(startPos, substring.count)
    }
}
GabeV
  • 859
  • 8
  • 21