How to group search regular expressions using swift - regex

In regular expressions you can group different matches to easily "pattern match" a given match.
while match != nil {
match = source.rangeOfString(regex, options: .RegularExpressionSearch)
if let m = match {
result.append(source.substringWithRange(m)
source.replaceRange(m, with: "")
}
}
The above works find to find a range of the match, but it cannot tell me the group. For instance if I search for words encapsulated in "" I would like to match a "word" but quickly fetch only word
Is it possible to do so in swift?

Swift is pretty ugly right now with regular expressions -- let's hope for more-native support soon! The method on NSRegularExpression you want is matchesInString. Here's how to use it:
let string = "This is my \"string\" of \"words\"."
let re = NSRegularExpression(pattern: "\"(.+?)\"", options: nil, error: nil)!
let matches = re.matchesInString(string, options: nil, range: NSRange(location: 0, length: string.utf16Count))
println("number of matches: \(matches.count)")
for match in matches as [NSTextCheckingResult] {
// range at index 0: full match
// range at index 1: first capture group
let substring = (string as NSString).substringWithRange(match.rangeAtIndex(1))
println(substring)
}
Output:
number of matches: 2
string
words

You can use this if you want to collect the matched strings.
(My answer is derived from Nate Cooks very helpful answer.)
Updated for Swift 2.1
extension String {
func regexMatches(pattern: String) -> Array<String> {
let re: NSRegularExpression
do {
re = try NSRegularExpression(pattern: pattern, options: [])
} catch {
return []
}
let matches = re.matchesInString(self, options: [], range: NSRange(location: 0, length: self.utf16.count))
var collectMatches: Array<String> = []
for match in matches {
// range at index 0: full match
// range at index 1: first capture group
let substring = (self as NSString).substringWithRange(match.rangeAtIndex(1))
collectMatches.append(substring)
}
return collectMatches
}
}
Updated for Swift 3.0
extension String {
func regexMatches(pattern: String) -> Array<String> {
let re: NSRegularExpression
do {
re = try NSRegularExpression(pattern: pattern, options: [])
} catch {
return []
}
let matches = re.matches(in: self, options: [], range: NSRange(location: 0, length: self.utf16.count))
var collectMatches: Array<String> = []
for match in matches {
// range at index 0: full match
// range at index 1: first capture group
let substring = (self as NSString).substring(with: match.rangeAt(1))
collectMatches.append(substring)
}
return collectMatches
}}

how about this guys, add as extension to String? )) all matches, all groups ) self = String if you want to add not as extension then add String parameter and replace all self to your parameter :)
func matchesForRegexInTextAll(regex: String!) -> [[String]] {
do {
let regex = try NSRegularExpression(pattern: regex, options: [])
let nsString = self as NSString
var resultsFinal = [[String]]()
let results = regex.matchesInString(self,
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 []
}
}

All the answers provided are good, but nonetheless I am going to provide my String extension written in Swift 2.2.
Noted differences:
only use the first match
supports multiple captured groups
a more accurate function name (it is capture groups, not matches)
.
extension String {
func capturedGroups(withRegex pattern: String) -> [String]? {
var regex: NSRegularExpression
do {
regex = try NSRegularExpression(pattern: pattern, options: [])
} catch {
return nil
}
let matches = regex.matchesInString(self, options: [], range: NSRange(location:0, length: self.characters.count))
guard let match = matches.first else { return nil }
// Note: Index 1 is 1st capture group, 2 is 2nd, ..., while index 0 is full match which we don't use
let lastRangeIndex = match.numberOfRanges - 1
guard lastRangeIndex >= 1 else { return nil }
var results = [String]()
for i in 1...lastRangeIndex {
let capturedGroupIndex = match.rangeAtIndex(i)
let matchedString = (self as NSString).substringWithRange(capturedGroupIndex)
results.append(matchedString)
}
return results
}
}
To use:
// Will match "bcde"
"abcdefg".capturedGroups(withRegex: "a(.*)f")

Updated for Swift 4
/**
String extension that extract the captured groups with a regex pattern
- parameter pattern: regex pattern
- Returns: captured groups
*/
public func capturedGroups(withRegex pattern: String) -> [String] {
var results = [String]()
var regex: NSRegularExpression
do {
regex = try NSRegularExpression(pattern: pattern, options: [])
} catch {
return results
}
let matches = regex.matches(in: self, options: [], range: NSRange(location:0, length: self.count))
guard let match = matches.first else { return results }
let lastRangeIndex = match.numberOfRanges - 1
guard lastRangeIndex >= 1 else { return results }
for i in 1...lastRangeIndex {
let capturedGroupIndex = match.range(at: i)
let matchedString = (self as NSString).substring(with: capturedGroupIndex)
results.append(matchedString)
}
return results
}
To use:
// Will match "bcde"
"abcdefg".capturedGroups(withRegex: "a(.*)f")
Gist on github: https://gist.github.com/unshapedesign/1b95f78d7f74241f706f346aed5384ff

Related

Swift 3 - How do I extract captured groups in regular expressions?

I am using Swift 3 and trying to access captured groups.
let regexp = "((ALREADY PAID | NOT ALR | PROVIDER MAY | READY | MAY BILL | BILL YOU | PAID)((.|\\n)*))(( \\d+)(\\.+|-+)(\\d\\d))"
// check if some substring is in the recognized text
if let range = stringText.range(of:regexp, options: .regularExpression) {
let result = tesseract.recognizedText.substring(with:range)
}
I want to be able to extract out the last two numbers captured (\d\d) so if the text was: ALREADY PAID asfasdfadsfasdf 39.15, it would extract 15. Here is a regex builder that shows what I want. Normally, I would be able to do $8 to get the 8th group that was extracted but I don't know how to do that in Swift 3.
http://regexr.com/3fh1e
Swift 4, Swift 5
extension String {
func groups(for regexPattern: String) -> [[String]] {
do {
let text = self
let regex = try NSRegularExpression(pattern: regexPattern)
let matches = regex.matches(in: text,
range: NSRange(text.startIndex..., in: text))
return matches.map { match in
return (0..<match.numberOfRanges).map {
let rangeBounds = match.range(at: $0)
guard let range = Range(rangeBounds, in: text) else {
return ""
}
return String(text[range])
}
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
}
example:
let res = "1my 2own 3string".groups(for:"(([0-9]+)[a-z]+) ")
(lldb) po res ▿ 2 elements
▿ 0 : 3 elements
- 0 : "1my "
- 1 : "1my"
- 2 : "1"
▿ 1 : 3 elements
- 0 : "2own "
- 1 : "2own"
- 2 : "2"
but I don't know how to do that in Swift 3.
When you receive a match from NSRegularExpression, what you get is an NSTextCheckingResult. You call rangeAt to get a specific capture group.
Example:
let s = "hey ho ha"
let pattern = "(h).*(h).*(h)"
// our goal is capture group 3, "h" in "ha"
let regex = try! NSRegularExpression(pattern: pattern)
let result = regex.matches(in:s, range:NSMakeRange(0, s.utf16.count))
let third = result[0].rangeAt(3) // <-- !!
third.location // 7
third.length // 1
As ever, a simple extension seems to be the way around swift's bizarre overcomplication...
extension NSTextCheckingResult {
func groups(testedString:String) -> [String] {
var groups = [String]()
for i in 0 ..< self.numberOfRanges
{
let group = String(testedString[Range(self.range(at: i), in: testedString)!])
groups.append(group)
}
return groups
}
}
Use it like this:
if let match = myRegex.firstMatch(in: someString, range: NSMakeRange(0, someString.count)) {
let groups = match.groups(testedString: someString)
//... do something with groups
}
A slightly altered version based on #Vyacheslav's answer with different error handling approach:
enum ParsingError: Error {
// You can pass more info here with parameter(s) if you want, e.g. `case let invalidRange(originalString, failedAtRange)`
case invalidRange
}
protocol StringUtilityRequired {
var stringUtility: StringUtility { get }
}
extension StringUtilityRequired {
var stringUtility: StringUtility { StringUtility() }
}
enum StringUtility {
func groups(_ str: String, pattern: String) throws -> [[String]] {
let regex = try NSRegularExpression(pattern: pattern)
let matches = regex.matches(in: str, range: NSRange(str.startIndex..., in: str))
return try matches.map { match throws in
return try (0 ..< match.numberOfRanges).map { range throws in
let rangeBounds = match.range(at: range)
guard let range = Range(rangeBounds, in: str) else {
throw ParsingError.invalidRange
}
return String(str[range])
}
}
}
// This component is stateless; it doesn't have any side effect
case pure
init() { self = .pure }
}
Usage:
struct MyComponent: StringUtilityRequired {
func myFunc() throws {
let groups = try stringUtility.groups("Test 123", pattern: "(.+)\s(.+)")
print(groups)
}
}

Youtube Video Id from URL - Swift3

Basically I have a Youtube URL as string, I want to extract the video Id from that URL. I found some code in objective c that is as below:
NSError *error = NULL;
NSRegularExpression *regex =
[NSRegularExpression regularExpressionWithPattern:#"?.*v=([^&]+)"
options:NSRegularExpressionCaseInsensitive
error:&error];
NSTextCheckingResult *match = [regex firstMatchInString:youtubeURL
options:0
range:NSMakeRange(0, [youtubeURL length])];
if (match) {
NSRange videoIDRange = [match rangeAtIndex:1];
NSString *substringForFirstMatch = [youtubeURL substringWithRange:videoIDRange];
}
When I am converting this code to swift3 that is:
var error: Error? = nil
var regex = try! NSRegularExpression(pattern: "?.*v=([^&]+)", options: .caseInsensitive)
var match = regex!.firstMatch(in: youtubeURL, options: [], range: NSRange(location: 0, length: youtubeURL.length))!
if match {
var videoIDRange = match.rangeAt(1)
var substringForFirstMatch = (youtubeURL as NSString).substring(with: videoIDRange)
}
Gives error as:
fatal error: 'try!' expression unexpectedly raised an error: Error Domain=NSCocoaErrorDomain Code=2048 "The value “?.*v=([^&]+)” is invalid."
Can anybody help me about this error or anybody explain how to get video id from url in Swift 3.
Thanks in advance
Safer version (without force unwrapping !):
extension String {
var youtubeID: String? {
let pattern = "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)"
let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive)
let range = NSRange(location: 0, length: count)
guard let result = regex?.firstMatch(in: self, range: range) else {
return nil
}
return (self as NSString).substring(with: result.range)
}
}
Examples:
"https://www.youtube.com/watch?v=C0DPdy98e4c".youtubeID // "C0DPdy98e4c"
"https://youtube.com/watch?v=C0DPdy98e4c".youtubeID // "C0DPdy98e4c"
"www.youtube.com/watch?v=C0DPdy98e4c".youtubeID // "C0DPdy98e4c"
"youtube.com/watch?v=C0DPdy98e4c".youtubeID // "C0DPdy98e4c"
"https://youtu.be/C0DPdy98e4c".youtubeID // "C0DPdy98e4c"
"youtu.be/C0DPdy98e4c".youtubeID // "C0DPdy98e4c"
Credits: Usman Nisar's answer
I have a different way of doing this using URLComponents. You then just select the 'v' parameter from the url, if it exists.
func getYoutubeId(youtubeUrl: String) -> String? {
return URLComponents(string: youtubeUrl)?.queryItems?.first(where: { $0.name == "v" })?.value
}
And then pass in a Youtube url like this:
print (getYoutubeId(youtubeUrl: "https://www.youtube.com/watch?v=Y7ojcTR78qE&spfreload=9"))
Swift 5
var youtubeURLs = [
"http://www.youtube.com/watch?v=-wtIMTCHWuI",
"http://www.youtube.com/v/-wtIMTCHWuI?version=3&autohide=1",
"http://youtu.be/-wtIMTCHWuI",
"http://www.youtube.com/oembed?url=http%3A//www.youtube.com/watch?v%3D-wtIMTCHWuI&format=json",
"https://youtu.be/uJ2PZaO1N5E",
"https://www.youtube.com/embed/M7lc1UVf-VE",
"http://www.youtube.com/attribution_link?a=JdfC0C9V6ZI&u=%2Fwatch%3Fv%3DEhxJLojIE_o%26feature%3Dshare",
"https://www.youtube.com/attribution_link?a=8g8kPrPIi-ecwIsS&u=/watch%3Fv%3DyZv2daTWRZU%26feature%3Dem-uploademail"
]
func getVideoID(from urlString: String) -> String? {
guard let url = urlString.removingPercentEncoding else { return nil }
do {
let regex = try NSRegularExpression.init(pattern: "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)", options: .caseInsensitive)
let range = NSRange(location: 0, length: url.count)
if let matchRange = regex.firstMatch(in: url, options: .reportCompletion, range: range)?.range {
let matchLength = (matchRange.lowerBound + matchRange.length) - 1
if range.contains(matchRange.lowerBound) &&
range.contains(matchLength) {
let start = url.index(url.startIndex, offsetBy: matchRange.lowerBound)
let end = url.index(url.startIndex, offsetBy: matchLength)
return String(url[start...end])
}
}
} catch {
print(error.localizedDescription)
}
return nil
}
for url in youtubeURLs {
print("Video id: \(getVideoID(from: url) ?? "NA") for url: \(url)")
}
Result:
Video id: -wtIMTCHWuI for url: http://www.youtube.com/watch?v=-wtIMTCHWuI
Video id: -wtIMTCHWuI for url: http://www.youtube.com/v/-wtIMTCHWuI?version=3&autohide=1
Video id: -wtIMTCHWuI for url: http://youtu.be/-wtIMTCHWuI
Video id: -wtIMTCHWuI for url: http://www.youtube.com/oembed?url=http%3A//www.youtube.com/watch?v%3D-wtIMTCHWuI&format=json
Video id: uJ2PZaO1N5E for url: https://youtu.be/uJ2PZaO1N5E
Video id: M7lc1UVf-VE for url: https://www.youtube.com/embed/M7lc1UVf-VE
Video id: EhxJLojIE_o for url: http://www.youtube.com/attribution_link?a=JdfC0C9V6ZI&u=%2Fwatch%3Fv%3DEhxJLojIE_o%26feature%3Dshare
Video id: yZv2daTWRZU for url: https://www.youtube.com/attribution_link?a=8g8kPrPIi-ecwIsS&u=/watch%3Fv%3DyZv2daTWRZU%26feature%3Dem-uploademail
Here is code to extract youtube video id from any youtube url:
(Swift)
func extractYoutubeId(fromLink link: String) -> String {
let regexString: String = "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)"
let regExp = try? NSRegularExpression(pattern: regexString, options: .caseInsensitive)
let array: [Any] = (regExp?.matches(in: link, options: [], range: NSRange(location: 0, length: (link.characters.count ))))!
if array.count > 0 {
let result: NSTextCheckingResult? = array.first as? NSTextCheckingResult
return (link as NSString).substring(with: (result?.range)!)
}
return ""
}
A Swift 4 version using the elegant flatMap:
func extractYouTubeId(from url: String) -> String? {
let typePattern = "(?:(?:\\.be\\/|embed\\/|v\\/|\\?v=|\\&v=|\\/videos\\/)|(?:[\\w+]+#\\w\\/\\w(?:\\/[\\w]+)?\\/\\w\\/))([\\w-_]+)"
let regex = try? NSRegularExpression(pattern: typePattern, options: .caseInsensitive)
return regex
.flatMap { $0.firstMatch(in: url, range: NSMakeRange(0, url.count)) }
.flatMap { Range($0.range(at: 1), in: url) }
.map { String(url[$0]) }
}
This method uses a regex that detects most of the possible YouTube URL formats (.be/*, /embed/, /v/ - you can find the full list here).
Your first problem is you are not escaping ? in your expression. ? is reserved character and if you want to use it in your expressions, you must escape with \ and since \ is used also to escape " character you must escape ? with double backslash something like \\?. So according to above information, following code correctly extracts the videoId
let youtubeURL = "https://www.youtube.com/watch?v=uH8o-JTHJdM"
let regex = try! NSRegularExpression(pattern: "\\?.*v=([^&]+)", options: .caseInsensitive)
let match = regex.firstMatch(in: youtubeURL, options: [], range: NSRange(location: 0, length: youtubeURL.characters.count))
if let videoIDRange = match?.rangeAt(1) {
let substringForFirstMatch = (youtubeURL as NSString).substring(with: videoIDRange)
} else {
//NO video URL
}
Some samples of Youtube's url:
let urls: [String] = [
"www.youtube-nocookie.com/embed/up_lNV-yoK4?rel=0",
"http://www.youtube.com/watch?v=peFZbP64dsU",
"http://www.youtube.com/watch?v=cKZDdG9FTKY&feature=channel",
"http://youtube.com/v/dQw4w9WgXcQ?feature=youtube_gdata_player",
"http://youtube.com/?v=dQw4w9WgXcQ&feature=youtube_gdata_player",
"http://youtu.be/6dwqZw0j_jY",
"http://youtu.be/dQw4w9WgXcQ?feature=youtube_gdata_playe",
"http://youtube.com/vi/dQw4w9WgXcQ?feature=youtube_gdata_player",
"http://youtube.com/?vi=dQw4w9WgXcQ&feature=youtube_gdata_player",
"http://youtube.com/watch?vi=dQw4w9WgXcQ&feature=youtube_gdata_player",
"http://www.youtube.com/user/Scobleizer#p/u/1/1p3vcRhsYGo?rel=0",
"http://www.youtube.com/user/SilkRoadTheatre#p/a/u/2/6dwqZw0j_jY",
"1p3vcRhsY02"
]
My extension based on Islam Q. solution:
private extension String {
var youtubeID: String? {
let pattern = "((?<=(v|V|vi)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=vi=)|(?<=/u/[0-9_]/)|(?<=embed/))([\\w-]++)"
let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive)
let range = NSRange(location: 0, length: count)
guard let result = regex?.firstMatch(in: self, range: range) else {
return count == 11 ? self : nil
}
let id = (self as NSString).substring(with: result.range)
return id.count == 11 ? id : nil
}
}
Recently youtube added a prefix to live videos.
I used this solution from the thread
https://stackoverflow.com/a/62651531/16457129
And I just added (?<=live/)
let regex = try NSRegularExpression.init(pattern: "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/)|(?<=live/))([\\w-]++)", options: .caseInsensitive)
To get video Id from Youtube Url, use code # Swift4 :
var videoId = ""
if youtubeLink.lowercased().contains("youtu.be"){
linkString = youtubeLink
if let range = linkString.range(of: "be/"){
videoId = youtubeLink[range.upperBound...].trimmingCharacters(in: .whitespaces)
}
}
else if youtubeLink.lowercased().contains("youtube.com"){
linkString = youtubeLink
if let range = linkString.range(of: "?v="){
videoId = youtubeLink[range.upperBound...].trimmingCharacters(in: .whitespaces)
}
}
Hope will be helping! :)

How to replace a string with its uppercase with NSRegularExpression in Swift?

I was trying to change hello_world to helloWorld by this snippet of code (Swift 3.0):
import Foundation
let oldLine = "hello_world"
let fullRange = NSRange(location: 0, length: oldLine.characters.count)
let newLine = NSMutableString(string: oldLine)
let regex = try! NSRegularExpression(pattern: "(_)(\\w)", options: [])
regex.replaceMatches(in: newLine, options: [], range: fullRange,
withTemplate: "\\L$2")
The result was newLine = "helloLworld"
I used "\\L$2" as template because I saw this answer: https://stackoverflow.com/a/20742304/5282792 saying \L$2 is the pattern for the second group's uppercase in replacement template. But it didn't work in NSRegularExpression.
So can I replace a string with its uppercase with a replacement template pattern in NSRegularExpression.
One way to work with your case is subclassing NSRegularExpression and override replacementString(for:in:offset:template:) method.
class ToUpperRegex: NSRegularExpression {
override func replacementString(for result: NSTextCheckingResult, in string: String, offset: Int, template templ: String) -> String {
guard result.numberOfRanges > 2 else {
return ""
}
let matchingString = (string as NSString).substring(with: result.rangeAt(2)) as String
return matchingString.uppercased()
}
}
let oldLine = "hello_world"
let fullRange = NSRange(0..<oldLine.utf16.count) //<-
let tuRegex = try! ToUpperRegex(pattern: "(_)(\\w)")
let newLine = tuRegex.stringByReplacingMatches(in: oldLine, range: fullRange, withTemplate: "")
print(newLine) //->helloWorld
This doesn't answer the question pertaining regex, but might be of interest for readers not necessarily needing to use regex to perform this task (rather, using native Swift)
extension String {
func camelCased(givenSeparators separators: [Character]) -> String {
let charChunks = characters.split { separators.contains($0) }
guard let firstChunk = charChunks.first else { return self }
return String(firstChunk).lowercased() + charChunks.dropFirst()
.map { String($0).onlyFirstCharacterUppercased }.joined()
}
// helper (uppercase first char, lowercase rest)
var onlyFirstCharacterUppercased: String {
let chars = characters
guard let firstChar = chars.first else { return self }
return String(firstChar).uppercased() + String(chars.dropFirst()).lowercased()
}
}
/* Example usage */
let oldLine1 = "hello_world"
let oldLine2 = "fOo_baR BAX BaZ_fOX"
print(oldLine1.camelCased(givenSeparators: ["_"])) // helloWorld
print(oldLine2.camelCased(givenSeparators: ["_", " "])) // fooBarBazBazFox

How to fetch words from text with regex in Swift iOS?

I have as string:
let inputText:String = "myemail_at_gmail.com_organizer#company.com"
I want to get in output: myemail#gmail.com
So I need to write 1st some pattern that matches this rule:
<email_prefix>_at_<domain>_organizer#company.com
after that I can combine:
<email_prefix>#<domain>
I use following class:
class Regex {
let internalExpression: NSRegularExpression
let pattern: String
init(_ pattern: String) {
self.pattern = pattern
var error: NSError?
self.internalExpression = NSRegularExpression(pattern: pattern, options: .CaseInsensitive, error: &error)!
}
func test(input: String) -> Bool {
let matches = self.internalExpression.matchesInString(input, options: nil, range:NSMakeRange(0, count(input)))
return matches.count > 0
}
}
and look for regex syntax:
if Regex("^\\w+_at_\\w+_organizer#company.com$") // id doesn't work
.test(inputText) {
let result:String = inputText.split("_at_")[0] + "#" + inputText.split("_at_")[1].split("_organizer#company.com")[0]
}
This one doesn't work: "^\\w+_at_\\w+_organizer#company.com$"
This one works but its not completed: "\\w+_organizer#company.com$"
Please help,
Ok, I found solution:
since i work with email, i need validate email name and email domain separatly:
let inputText:String = "myemail_at_gmail.com_organizer#company.com"
if Regex("^[A-Z0-9a-z._%+-]+_at_[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}_organizer#company.com$")
.test(inputText) {
let result:String = inputText.split("_at_")[0] + "#" +
inputText.split("_at_")[1].split("_organizer#company.com")[0]
print(result) // myemail#gmail.com
}

How to extract all email address from text in swift

I have a text
var txt = "+abc#gmail.com heyyyyy cool +def#gmail.com"
I want to extract the email address from the text and store it in an array. I want to do it with regular expression. I found the regular expression, but i am not able to save the email in to an array.
i tried
let regEx = "/(\\+[a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\\.[a-zA-Z0-9._-]+)/gi"
if let email = NSPredicate(format: "SELF MATCHES %#", regEx) {
//what to do here
}
Or am i doing wrong?
I know this is a basic question. Please help
Thanks in advance.
These code worked for me. You can checkout email regex from here.
SWIFT 5
func extractEmailAddrIn(text: String) -> [String] {
var results = [String]()
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let nsText = text as NSString
do {
let regExp = try NSRegularExpression(pattern: emailRegex, options: .caseInsensitive)
let range = NSMakeRange(0, text.count)
let matches = regExp.matches(in: text, options: .reportProgress, range: range)
for match in matches {
let matchRange = match.range
results.append(nsText.substring(with: matchRange))
}
} catch (let error) {
print(error)
}
return results
}
SWIFT 3
func extractEmailAddrIn(text: String) -> [String] {
var results = [String]()
let emailRegex = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let nsText = text as NSString
do {
let regExp = try NSRegularExpression(pattern: emailRegex, options: NSRegularExpressionOptions.CaseInsensitive)
let range = NSMakeRange(0, text.characters.count)
let matches = regExp.matchesInString(text, options: .ReportProgress, range: range)
for match in matches {
let matchRange = match.range
results.append(nsText.substringWithRange(matchRange))
}
} catch _ {
}
return results
}
I recommend using the NSRegularExpression class instead of NSPredicate. The format for the regular expressions is from the ICU.
Here is one way to do it:
let pattern = "(\\+[a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\\.[a-zA-Z0-9._-]+)"
let regexp = NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions.CaseInsensitive, error: nil)
let str = "+abc#gmail.com heyyyyy cool +def#gmail.com" as NSString
var results = [String]()
regexp?.enumerateMatchesInString(str, options: NSMatchingOptions(0), range: NSRange(location: 0, length: str.length), usingBlock: { (result: NSTextCheckingResult!, _, _) in
results.append(str.substringWithRange(result.range))
})
// Gives [+abc#gmail.com, +def#gmail.com]
Looks like your regex name is wrong. You declare it as regEx but in your NSPredicate you use emailRegEx.
Do you need the plus sign in the mail address?
Without + sign in the address:
([a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\\.[a-zA-Z0-9._-]+)
Result: ["abc#gmail.com", "def#gmail.com"]
With + sign in the address:
([\\+a-zA-Z0-9._-]+#[a-zA-Z0-9._-]+\\.[a-zA-Z0-9._-]+)
Result: ["+abc#gmail.com", "+def#gmail.com"]
Here is the String extensions I have created to extract emails from It works well in swift 4.
extension String {
func getEmails() -> [String] {
if let regex = try? NSRegularExpression(pattern: "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}", options: .caseInsensitive)
{
let string = self as NSString
return regex.matches(in: self, options: [], range: NSRange(location: 0, length: string.length)).map {
string.substring(with: $0.range).lowercased()
}
}
return []
}
}
Usage
let test = "Precision Construction John Smith cONSTRUCTION WORKER 123 Main St, Ste. 30o
www.precsioncontructien.com adil#gmail.com fraz#gmail.pk janesmit#precisiencenstruction.com 555.555.5SS5"
let emails = test.getEmails()
print(emails)
// results ["adil#gmail.com", "fraz#gmail.pk", "janesmit#precisiencenstruction.com"]