real 19
real 19

Reputation: 748

Exclude certain matches using NSRegularExpression

I am following http://www.raywenderlich.com/86205/nsregularexpression-swift-tutorial and using the playground file below:

http://cdn5.raywenderlich.com/wp-content/uploads/2015/01/iRegex-Playground-Xcode-6.3.zip

to help find matches , but I need to be able to exclude certain results .

Basically I am looking at the following pattern:

let thenotClasses = "*121:32,  Malachi 22:66 , 32:434, 16:111 , 17:11 , John 13:14, Verse 41:29, Great 71:21"

listMatches("\\d\\d?\\d?:\\d\\d?\\d?", inString: thenotClasses)

I get all the number:number matches , however, what I really want to do is to also tell it to exclude any matches prefixed with '*' or matches which are preceded by the word "Malachi " or "John " but include the rest

So in this case I want the matches to return:

[32:434, 16:111 , 17:11 , 41:29  and 71:21]

Any help would be greatly appreciated, God willing :)

Upvotes: 0

Views: 852

Answers (1)

Code Different
Code Different

Reputation: 93171

A RegEx pattern that invalidates a match when preceded by certain words are hard to write, mostly because the regex engine is greedy so it can start with the next digit.

If you use a negative look-behind:

(?<!\*|Malachi |John )(\d+:\d+)

Which means "match digits not preceded by *, Malachi or John" the match will just begin from the next digit. In Malachi 22:66 for example, it will capture 2:66.

The most common pitfalls I've seen with Regex usage is to delegate everything to the regex engine. It is powerful indeed but you forgot you also have vastly more flexible programming language that calls the regex.

Here's an idea that mixes the two together: capture any number:number and check what goes before it. Exclude the match if it is preceded by * or Malachi or John.

Pattern:

(\*|Malachi |John )?(\d+:\d+)

(\*|Malachi |John ) - match a *, Malachi or John and put it into capture group 1
?                   - make the first capture group optional
(\d+:\d+)           - match the verse and put it into capture group 2

Code:

let str = "*121:32,  Malachi 22:66 , 32:434, 16:111 , 17:11 , John 13:14, Verse 41:29, Great 71:21"
let s = str as NSString  // NSString is easier to work with Regex

let regex = try! NSRegularExpression(pattern: "(\\*|Malachi |John )?(\\d+:\\d+)", options: [])
var verses = [String]()

regex.enumerateMatchesInString(str, options: [], range: NSMakeRange(0, str.characters.count)) { result, flags, stop in
   // Check that the first capture group is not found. Otherwise, return
    guard let result = result where result.rangeAtIndex(1).location == NSNotFound else {
        return
    }

    // When the first capture group is not found, add the second capture, group
    // i.e. the verse number, to the result list
    verses.append(s.substringWithRange(result.rangeAtIndex(2)))
}

print(verses)

Upvotes: 1

Related Questions