Using Regex in Swift

Swift extract regex matches

Even if the matchesInString() method takes a String as the first argument,
it works internally with NSString, and the range parameter must be given
using the NSString length and not as the Swift string length. Otherwise it will
fail for "extended grapheme clusters" such as "flags".

As of Swift 4 (Xcode 9), the Swift standard
library provides functions to convert between Range<String.Index>
and NSRange.

func matches(for regex: String, in text: String) -> [String] {

do {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: text,
range: NSRange(text.startIndex..., in: text))
return results.map {
String(text[Range($0.range, in: text)!])
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}

Example:

let string = "€4€9"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]

Note: The forced unwrap Range($0.range, in: text)! is safe because
the NSRange refers to a substring of the given string text.
However, if you want to avoid it then use

        return results.flatMap {
Range($0.range, in: text).map { String(text[$0]) }
}

instead.


(Older answer for Swift 3 and earlier:)

So you should convert the given Swift string to an NSString and then extract the
ranges. The result will be converted to a Swift string array automatically.

(The code for Swift 1.2 can be found in the edit history.)

Swift 2 (Xcode 7.3.1) :

func matchesForRegexInText(regex: String, text: String) -> [String] {

do {
let regex = try NSRegularExpression(pattern: regex, options: [])
let nsString = text as NSString
let results = regex.matchesInString(text,
options: [], range: NSMakeRange(0, nsString.length))
return results.map { nsString.substringWithRange($0.range)}
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return []
}
}

Example:

let string = "€4€9"
let matches = matchesForRegexInText("[0-9]", text: string)
print(matches)
// ["4", "9"]

Swift 3 (Xcode 8)

func matches(for regex: String, in text: String) -> [String] {

do {
let regex = try NSRegularExpression(pattern: regex)
let nsString = text as NSString
let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
return results.map { nsString.substring(with: $0.range)}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}

Example:

let string = "€4€9"
let matched = matches(for: "[0-9]", in: string)
print(matched)
// ["4", "9"]

Regular expressions in swift

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"]

Use regular expression in Swift String.contains() func

You are quite close

if thisString.range(of: "[^a-zA-Z0-9-]", options: .regularExpression) != nil { 
//reject
} else {
//accept
}

How to use regex with Swift?


ORIGINAL ANSWER

Here is a function you can leverage to get captured group texts:

import Foundation

extension String {
func firstMatchIn(string: NSString!, atRangeIndex: Int!) -> String {
var error : NSError?
let re = NSRegularExpression(pattern: self, options: .CaseInsensitive, error: &error)
let match = re.firstMatchInString(string, options: .WithoutAnchoringBounds, range: NSMakeRange(0, string.length))
return string.substringWithRange(match.rangeAtIndex(atRangeIndex))
}
}

And then:

var result = "&l=(\\d{8,})".firstMatchIn(yourAccountNumber, atRangeIndex: 1)

The 1 in atRangeIndex: 1 will extract the text captured by (\d{8,}) capture group.

NOTE1: If you plan to extract 8, and only 8 digits after &l=, you do not need the , in the limiting quantifier, as {8,} means 8 or more. Change to {8} if you plan to capture just 8 digits.

NOTE2: NSMatchingAnchored is something you would like to avoid if your expected result is not at the beginning of a search range. See documentation:

Specifies that matches are limited to those at the start of the search range.

NOTE3: Speaking about "simplest" things, I'd advise to avoid using look-arounds whenever you do not have to. Look-arounds usually come at some cost to performance, and if you are not going to capture overlapping text, I'd recommend to use capture groups.

UPDATE FOR SWIFT 2

I have come up with a function that will return all matches with all capturing groups (similar to preg_match_all in PHP). Here is a way to use it for your scenario:

func regMatchGroup(regex: String, text: String) -> [[String]] {
do {
var resultsFinal = [[String]]()
let regex = try NSRegularExpression(pattern: regex, options: [])
let nsString = text as NSString
let results = regex.matchesInString(text,
options: [], range: NSMakeRange(0, nsString.length))
for result in results {
var internalString = [String]()
for var i = 0; i < result.numberOfRanges; ++i{
internalString.append(nsString.substringWithRange(result.rangeAtIndex(i)))
}
resultsFinal.append(internalString)
}
return resultsFinal
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return [[]]
}
}
// USAGE:
let yourAccountNumber = "index.php?page=index&l=99182677"
let matches = regMatchGroup("&l=(\\d{8,})", text: yourAccountNumber)
if (matches.count > 0) // If we have matches....
{
print(matches[0][1]) // Print the first one, Group 1.
}

Using regex in Swift

You may use

return word.replacingOccurrences(of: "\\W+", with: "", options: .regularExpression)

Note the options: .regularExpression argument that actually enables regex-based search in the .replacingOccurrences method.

Your pattern is [^\w]. It is a negated character class that matches any char but a word char. So, it is equal to \W.

The /.../ are regex delimiters. In Swift regex, they are parsed as literal forward slashes, and thus your pattern did not work.

The g is a "global" modifier that let's a regex engine match multiple occurrences, but it only works where it is supported (e.g. in JavaScript). Since regex delimiters are not supported in Swift regex, the regex engine knows how to behave through the .replacingOccurrences method definition:

Returns a new string in which all occurrences of a target string in the receiver are replaced by another given string.

If you need to check ICU regex syntax, consider referring to ICU User Guide > Regular Expressions, it is the regex library used in Swift/Objective-C.

Replace matches using regex by modifying matched string in swift

You may use

var value: NSMutableString = "It is live now at Germany(DE) or (SOFE)"
let pattern = "(?<=\\G(?<!\\A)|\\()[A-Za-z](?=[A-Za-z]+\\))"
let regex = try? NSRegularExpression(pattern: pattern)
regex?.replaceMatches(in: value, options: .reportProgress, range: NSRange(location: 0,length: value.length), withTemplate: "$0 ")
print(value)

Or just

let val =  "It is live now at Germany(DE) or (SOFE)"
let pattern = "(?<=\\G(?<!\\A)|\\()[A-Za-z](?=[A-Za-z]+\\))"
print( val.replacingOccurrences(of: pattern, with: "$0 ", options: .regularExpression, range: nil) )

Output: It is live now at Germany(D E) or (S O F E)

Pattern details

  • (?<=\\G(?<!\\A)|\\() - a positive lookbehind that matches a location right after ( or at the end of the preceding successful match
  • [A-Za-z] - matches and consumes any ASCII letter
  • (?=[A-Za-z]+\\)) - a positive lookahead that matches a location that is immediately followed with 1+ ASCII letters and then a ) char.

The $0 in the replacement inserts the whole match value back into the resulting string.

Swift - Regex to extract value

I guess you want to remove the tags.

If the backslash is only virtual the pattern is pretty simple: Basically <em> with optional slash /?

let trimmedString = string.replacingOccurrences(of: "</?em>", with: "", options: .regularExpression)

Considering also the backslash it's

let trimmedString = string.replacingOccurrences(of: "<\\\\?/?em>", with: "", options: .regularExpression)

If you want to extract only Furnished you have to capture groups: The string between the tags and everything after the closing tag until the next whitespace character.

let string = "Fully <em>Furni<\\/em>shed |Downtown and Canal Views"
let pattern = "<em>(.*)<\\\\?/em>(\\S+)"
do {
let regex = try NSRegularExpression(pattern: pattern)
if let match = regex.firstMatch(in: string, range: NSRange(string.startIndex..., in: string)) {
let part1 = string[Range(match.range(at: 1), in: string)!]
let part2 = string[Range(match.range(at: 2), in: string)!]
print(String(part1 + part2))
}
} catch { print(error) }

(Swift) How to find all strings that matches regex in string

You would need to manually find all occurrences in your string using a while condition similar to the one used in this post and get the string subsequences instead of its range:

func findSrcs(_ content: String) -> [Substring] {
let pattern = #"(?<=src=")[^"]+"#
var srcs: [Substring] = []
var startIndex = content.startIndex
while let range = content[startIndex...].range(of: pattern, options: .regularExpression) {
srcs.append(content[range])
startIndex = range.upperBound
}
return srcs
}

Playground testing:

let content = """
<span>whatever</span>
<img src="smiley.gif" alt="Smiley face">
<span>whatever</span>
<img src="stackoverflow.jpg" alt="Stack Overflow">
"""

print(findSrcs(content))

This will print

["smiley.gif", "stackoverflow.jpg"]

Regex not working in swift. Giving an error invalid regex

Curly braces are special characters which have to be escaped

\{[^}]*\}, in a Swift literal string \\{[^}]*\\}

By the way don't use the literal initializer of NSRange to get the length of the string, the highly recommended way is

static func matches(regex: String, text: String) -> Bool {
do {
let regex = try NSRegularExpression(pattern: regex, options: .caseInsensitive)
let match = regex.firstMatch(in: text, options: [],
range: NSRange(text.startIndex..., in: text)
return match != nil
} catch {
print("invalid regex: \(error.localizedDescription)")
return false
}
}

Regular expression to find a number ends with special character in Swift

You can use

func isValidItem(_ item: String) -> Bool {
let pattern = #"^[0-9]+\$\z"#
return (item.range(of: pattern, options: .regularExpression) != nil)
}

let arr = ["Foo", "Foo1", "Foo$", "$Foo", "1Foo", "1$", "20$", "1$Foo", "12$$"]

print(arr.filter {isValidItem($0)})
// => ["1$", "20$"]

Here,

  • ^ - matches start of a line
  • [0-9]+ - one or more ASCII digits (note that Swift regex engine is ICU and \d matches any Unicode digits in this flavor, so [0-9] is safer if you need to only match digits from the 0-9 range)
  • \$ - a $ char
  • \z - the very end of string.

See the online regex demo ($ is used instead of \z since the demo is run against a single multiline string, hence the use of the m flag at regex101.com).



Related Topics



Leave a reply



Submit