Regular expressions in swift
Asked Answered
W

5

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.

Wakerly answered 18/5, 2019 at 2:49 Comment(5)
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.United
Also note that task 3 should be a separate question from the first two tasks.United
It's a just user input may or may not given. for instance without quotes. Thanks for response @rmaddy.Wakerly
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?Unsearchable
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.Unsearchable
B
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"]
Bedspread answered 18/5, 2019 at 3:21 Comment(2)
Thank you @Rajeshkumar. Can you help me to understand Rex also?. When I'm testing VSCode ex working but not in swift.Wakerly
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.United
F
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);
Ferne answered 18/5, 2019 at 3:18 Comment(2)
Not my vote but note that answers need to be in Swift, not JavaScript.United
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.Unsearchable
B
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"]
Bedspread answered 18/5, 2019 at 3:21 Comment(2)
Thank you @Rajeshkumar. Can you help me to understand Rex also?. When I'm testing VSCode ex working but not in swift.Wakerly
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.United
W
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
Wakerly answered 18/5, 2019 at 10:21 Comment(0)
F
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!")
}
Foredeck answered 18/5, 2019 at 5:12 Comment(0)
U
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*+(?=,|\\)$)"
Unsearchable answered 18/5, 2019 at 5:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.