5

I'm bit confused by NSRegularExpression in swift, can any one help me?

task:1 given ("name","john","name of john")
then I should get ["name","john","name of john"]. Here I should avoid the brackets.

task:2 given ("name"," john","name of john")
then I should get ["name","john","name of john"]. Here I should avoid the brackets and extra spaces and finally get array of strings.

task:3 given key = value // comment
then I should get ["key","value","comment"]. Here I should get only strings in the line by avoiding = and //
I have tried below code for task 1 but not passed.

let string = "(name,john,string for user name)"
let pattern = "(?:\\w.*)"

do {
    let regex = try NSRegularExpression(pattern: pattern, options: .caseInsensitive)
    let matches = regex.matches(in: string, options: [], range: NSRange(location: 0, length: string.utf16.count))
    for match in matches {
        if let range = Range(match.range, in: string) {
            let name = string[range]
            print(name)
        }
    }
} catch {
    print("Regex was bad!")
}


Thanks in advance.

Emma
  • 27,428
  • 11
  • 44
  • 69
Damodar
  • 707
  • 2
  • 10
  • 23
  • 1
    The `string` value in the code you posted does not match any of the "given" tasks in your question. One has quotes, the other doesn't. – rmaddy May 18 '19 at 02:52
  • Also note that task 3 should be a separate question from the first two tasks. – rmaddy May 18 '19 at 02:52
  • It's a just user input may or may not given. for instance without quotes. Thanks for response @rmaddy. – Damodar May 18 '19 at 02:54
  • Your task 1 uses `("name","john","name of john")` but your code snippet is using `(name,john,string for user name)`. Which is it? With quotes around the substrings or without? – Rob May 18 '19 at 05:16
  • Also, you’ve give examples where your substrings do not, themselves, include quotation marks or commas. But what if they did (e.g. with some escape character within the substrings). If you need to handle that, you’re starting to enter a territory where regex might not be the best approach at all. – Rob May 18 '19 at 05:20

5 Answers5

3

RegEx in Swift

These posts might help you to explore regular expressions in swift:

Task 1 & 2

This expression might help you to match your desired outputs for both Task 1 and 2:

"(\s+)?([a-z\s]+?)(\s+)?"

enter image description here


Based on Rob's advice, you could much reduce the boundaries, such as the char list [a-z\s]. For example, here, we can also use:

"(\s+)?(.*?)(\s+)?"

or

"(\s+)?(.+?)(\s+)?"

to simply pass everything in between two " and/or space.

enter image description here

RegEx

If this wasn't your desired expression, you can modify/change your expressions in regex101.com.

RegEx Circuit

You can also visualize your expressions in jex.im:

enter image description here

JavaScript Demo

const regex = /"(\s+)?([a-z\s]+?)(\s+)?"/gm;
const str = `"name","john","name of john"
"name","       john","name of john"
"       name  ","       john","name of john     "
"       name  ","       john","       name of john     "`;
const subst = `\n$2`;

// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);

console.log('Substitution result: ', result);

Task 3

This expression might help you to design an expression for the third task:

(.*?)([a-z\s]+)(.*?)

enter image description here

const regex = /(.*?)([a-z\s]+)(.*?)/gm;
const str = `key = value // comment
key = value with some text // comment`;
const subst = `$2,`;

// The substituted value will be contained in the result variable
const result = str.replace(regex, subst);

console.log('Substitution result: ', result);
Community
  • 1
  • 1
Emma
  • 27,428
  • 11
  • 44
  • 69
  • 3
    Not my vote but note that answers need to be in Swift, not JavaScript. – rmaddy May 18 '19 at 03:29
  • 1
    I wouldn’t use `a-z`. That won’t accept strings with accents. E.g., if the name was “José”. I’d just search for `[^"]+`. Also, no need to capture the white space before and after the target string. – Rob May 18 '19 at 04:01
2

Separate the string by non alpha numeric characters except white spaces. Then trim the elements with white spaces.

extension String {
    func words() -> [String] {
        return self.components(separatedBy: CharacterSet.alphanumerics.inverted.subtracting(.whitespaces))
                .filter({ !$0.isEmpty })
                .map({ $0.trimmingCharacters(in: .whitespaces) })
    }
}

let string1 = "(name,john,string for user name)"
let string2 = "(name,       john,name of john)"
let string3 = "key = value // comment"

print(string1.words())//["name", "john", "string for user name"]
print(string2.words())//["name", "john", "name of john"]
print(string3.words())//["key", "value", "comment"]
RajeshKumar R
  • 15,445
  • 2
  • 38
  • 70
  • Thank you @Rajeshkumar. Can you help me to understand Rex also?. When I'm testing VSCode ex working but not in swift. – Damodar May 18 '19 at 03:26
  • 3
    Keep in mind that this solution will fail for many likely values in string 3. And it can fail for any names with hyphens or apostrophes or other punctuation. – rmaddy May 18 '19 at 03:28
1

Here I have done with after understanding all of above comments.

let text = """
Capturing and non-capturing groups are somewhat advanced topics. You’ll encounter examples of capturing and non-capturing groups later on in the tutorial
"""

extension String {
            func  rex (_ expr : String)->[String] {
                return try! NSRegularExpression(pattern: expr, options: [.caseInsensitive])
                .matches(in: self, options: [], range: NSRange(location: 0, length: self.count))
                    .map {
                        String(self[Range($0.range, in: self)!])
                }
            }
        }
let r = text.rex("(?:\\w+-\\w+)") // pass any rex
Damodar
  • 707
  • 2
  • 10
  • 23
0

A single pattern, works for test:1...3, in Swift.

let string =
    //"(name,john,string for user name)" //test:1
    //#"("name","       john","name of john")"# //test:2
    "key = value // comment" //test:3

let pattern = #"(?:\w+)(?:\s+\w+)*"# //Swift 5+ only
//let pattern = "(?:\\w+)(?:\\s+\\w+)*"

do {
    let regex = try NSRegularExpression(pattern: pattern)
    let matches = regex.matches(in: string, range: NSRange(0..<string.utf16.count))
    let matchingWords = matches.map {
        String(string[Range($0.range, in: string)!])
    }
    print(matchingWords) //(test:3)->["key", "value", "comment"]
} catch {
    print("Regex was bad!")
}
OOPer
  • 47,149
  • 6
  • 107
  • 142
0

Let’s consider:

let string = "(name,José,name is José)"

I’d suggest a regex that looks for strings where:

  • It’s the substring either after the ( at the start of the full string or after a comma, i.e., look behind assertion of (?<=^\(|,);
  • It’s the substring that does not contain , within it, i.e., [^,]+?;
  • It’s the substring that is terminated by either a comma or ) at the end of the full string, i.e., look ahead assertion of (?=,|\)$), and
  • If you want to have it skip white space before and after the substrings, throw in the \s*+, too.

Thus:

let pattern = #"(?<=^\(|,)\s*+([^,]+?)\s*+(?=,|\)$)"#
let regex = try! NSRegularExpression(pattern: pattern)
regex.enumerateMatches(in: string, range: NSRange(string.startIndex..., in: string)) { match, _, _ in
    if let nsRange = match?.range(at: 1), let range = Range(nsRange, in: string) {
        let substring = String(string[range])
        // do something with `substring` here
    }
}

Note, I’m using the Swift 5 extended string delimiters (starting with #" and ending with "#) so that I don’t have to escape my backslashes within the string. If you’re using Swift 4 or earlier, you’ll want to escape those back slashes:

let pattern = "(?<=^\\(|,)\\s*+([^,]+?)\\s*+(?=,|\\)$)"
Rob
  • 415,655
  • 72
  • 787
  • 1,044