1

Swift 3 introduced String.range(of:options). Then, with this function, is possible match a part of string without creating a NSRegularExpression object, for example:

let text = "it is need #match my both #hashtag!"
let match = text.range(of: "(?:^#|\\s#)[\\p{L}0-9_]*", options: .regularExpression)!

print(text[match]) //  #math

But, is possible match both occurrences of the regexp (that is, #match and #hashtag), instead of only the first?

Leo Dabus
  • 198,248
  • 51
  • 423
  • 494
macabeus
  • 3,268
  • 2
  • 28
  • 49
  • https://stackoverflow.com/questions/32305891/index-of-a-substring-in-a-string-with-swift/32306142#32306142 you would need to implement it yourself. Check the method ranges(of:) how I've accomplished this – Leo Dabus Oct 21 '17 at 05:15
  • @LeoDabus I'm looking for a vanilla solution, without extension or overengineering. My case is simple, and I'll use it in one situation. But, thank you. – macabeus Oct 21 '17 at 05:19
  • If you want to use that method you don't have another option other than implementing it yourself – Leo Dabus Oct 21 '17 at 05:20
  • 1
    Another option would be to use NSRegularExpression method matches(in:). You can check Martin's answer https://stackoverflow.com/a/27880748/2303865 – Leo Dabus Oct 21 '17 at 05:22
  • 1
    Btw, your regex can be written as `(? – Wiktor Stribiżew Oct 21 '17 at 07:07

1 Answers1

2
let text = "it is need #match my both #hashtag!"
// create an object to store the ranges found
var ranges: [Range<String.Index>] = []
// create an object to store your search position
var start = text.startIndex
// create a while loop to find your regex ranges
while let range = text.range(of: "(?:^#|\\s#)[\\p{L}0-9_]*", options: .regularExpression, range: start..<text.endIndex) {
    // append your range found
    ranges.append(range)
    // and change the startIndex of your string search
    start = range.lowerBound < range.upperBound ? range.upperBound : text.index(range.lowerBound, offsetBy: 1, limitedBy: text.endIndex) ?? text.endIndex
}
ranges.forEach({print(text[$0])})

This will print

#match

#hashtag

If you need to use it more than once in your code you should add this extension to your project:

extension StringProtocol {
    func ranges<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Range<Index>] {
        var result: [Range<Index>] = []
        var start = startIndex
        while start < endIndex,
            let range = self[start...].range(of: string, options: options) {
            result.append(range)
            start = range.lowerBound < range.upperBound ?
                    range.upperBound : index(after: range.lowerBound)
        }
        return result
    }
}

usage:

let text = "it is need #match my both #hashtag!"
let pattern = "(?<!\\S)#[\\p{L}0-9_]*"
let ranges = text.ranges(of: pattern, options: .regularExpression)
let matches = ranges.map{text[$0]}
print(matches)  // ["#match", "#hashtag"]
Leo Dabus
  • 198,248
  • 51
  • 423
  • 494
  • Thank you! I really can't understand why the default library don't have this useful function. It have `range`, but haven't `ranges`. – macabeus Oct 21 '17 at 05:33
  • 1
    You are welcome. At least we can modify the language as needed. You can add the ranges extension from the link I posted to your project and use it as needed – Leo Dabus Oct 21 '17 at 05:34
  • How does this do regex matching? I don't get how this works. – SilverWolf Nov 24 '17 at 21:09
  • @seaturtle it uses String method range of string https://developer.apple.com/documentation/foundation/nsstring/1416849-range. – Leo Dabus Nov 24 '17 at 21:12
  • Oh, cool! I didn't know that existed, thanks. (: (I'm just looking for simple `String` regex matching.) – SilverWolf Nov 24 '17 at 21:13