jwasher
jwasher

Reputation: 429

Swift 2 NSRegularExpression

Eventually I want to be able to input a string like "\mycard{front1}{back1} \mycard{front2}{back2} \mycard{front3}{back3}" and return the front and back of each card.

I found this website on NSRegularExpression, but I'm having a hard time adjusting it to my problem.

Here is what I have so far.

import Foundation

func rangeFromNSRange(nsRange: NSRange, forString str: String) -> Range<String.Index>? {
    let fromUTF16 = str.utf16.startIndex.advancedBy(nsRange.location, limit: str.utf16.endIndex)
    let toUTF16 = fromUTF16.advancedBy(nsRange.length, limit: str.utf16.endIndex)

    if let from = String.Index(fromUTF16, within: str), let to = String.Index(toUTF16, within: str) {
        return from ..< to
    }

    return nil
}

do {
    // let input = "My name is Taylor Swift"
    // let regex = try NSRegularExpression(pattern: "My name is (.*)", options: NSRegularExpressionOptions.CaseInsensitive)

    let input = "mycard{front}{back}"
    let regex = try NSRegularExpression(pattern: "mycard{(.*)}{(.*)}", options: NSRegularExpressionOptions.CaseInsensitive)

    let matches = regex.matchesInString(input, options: [], range: NSMakeRange(0, input.characters.count))

    if let match = matches.first {
        let range = match.rangeAtIndex(1)
        if let swiftRange = rangeFromNSRange(range, forString: input) {
            let name = input.substringWithRange(swiftRange)
        }
    }
} catch {
    // regex was bad!
}

Upvotes: 1

Views: 500

Answers (2)

jwasher
jwasher

Reputation: 429

I gave up on regex. I just don't think it will do the trick here. I came up with another solution.

import Foundation

extension String {
    subscript (r: Int) -> Character? {
        var cur = 0
        for char in self.characters {
            if cur == r {
                return char
            }
            cur += 1
        }
        return nil
    }

    subscript (r: Range<Int>) -> String {
        return substringWithRange(Range(start: startIndex.advancedBy(r.startIndex), end: startIndex.advancedBy(r.endIndex)))
    }

    func parseBrackets () -> [String]? {
        var list: [String] = []
        var level = 0
        var start = 0
        for var i=0; i < self.characters.count - 1; i++ {
            if self[i] == "{" {
                level += 1
                if level == 1 {
                    start = i + 1
                }
            } else if self[i] == "}" {
                if level == 1 {
                    list.append(self[start..<i])
                }
                level -= 1
            }
        }

        if list.count > 0 {
            return list
        } else {
            return nil
        }
    }
}



let testString = "mycard{f{}ront}{termins{x}{n}} mycard{front1}{back1} mycard{front2}{back2}"
let list = testString.parseBrackets()

for a in list! {
    print(a)
}

Which gives the desired output

f{}ront
termins{x}{n}
front1
back1
front2

Upvotes: 0

luk2302
luk2302

Reputation: 57124

As stated in my comment you need to escape the { and }. That results in the following regex: mycard\\{(.*)\\}\\{(.*)\\}.

You then might want to change your match logic a little bit to output the expected results:

if let match = matches.first {
    for i in 1..<match.numberOfRanges {
        let range = match.rangeAtIndex(i)
        if let swiftRange = rangeFromNSRange(range, forString: input) {
            let name = input.substringWithRange(swiftRange)
            print(name)
        }
    }
}

Which outputs

front
back

If you want to match multiple cards use the following regex:

mycard\\{([^{]*)\\}\\{([^{]*)\\}

Then iterate over the matches

for match in matches {
    for i in 1..<match.numberOfRanges {
        let range = match.rangeAtIndex(i)
        if let swiftRange = rangeFromNSRange(range, forString: input) {
            let name = input.substringWithRange(swiftRange)
            print(name)
        }
    }
}

For the input mycard{front}{back} mycard{front1}{back1} the output correctly is

front
back
front1
back1

Upvotes: 3

Related Questions