regulares regular matches first expressions expresiones example ios regex string swift

ios - matches - swift 4 regular expression example



Partidos de expresiones regulares de extracto rรกpido (11)

Quiero extraer subcadenas de una cadena que coincida con un patrón regex.

Entonces estoy buscando algo como esto:

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

Entonces esto es lo que tengo:

func matchesForRegexInText(regex: String!, text: String!) -> [String] { var regex = NSRegularExpression(pattern: regex, options: nil, error: nil) var results = regex.matchesInString(text, options: nil, range: NSMakeRange(0, countElements(text))) as Array<NSTextCheckingResult> /// ??? return ... }

El problema es que matchesInString me entrega una matriz de NSTextCheckingResult , donde NSTextCheckingResult.range es de tipo NSRange .

NSRange es incompatible con Range<String.Index> , por lo que me impide usar text.substringWithRange(...)

¿Alguna idea de cómo lograr esto tan rápido sin demasiadas líneas de código?


La forma más rápida de devolver todos los partidos y capturar grupos en Swift 5

extension String { func match(_ regex: String) -> [[String]] { let nsString = self as NSString return (try? NSRegularExpression(pattern: regex, options: []))?.matches(in: self, options: [], range: NSMakeRange(0, count)).map { match in (0..<match.numberOfRanges).map { match.range(at: $0).location == NSNotFound ? "" : nsString.substring(with: match.range(at: $0)) } } ?? [] } }

Devuelve una matriz de cadenas de 2 dimensiones:

"prefix12suffix fix1su".match("fix([0-9]+)su")

devoluciones...

[["fix12su", "12"], ["fix1su", "1"]] // First element of sub-array is the match // All subsequent elements are the capture groups


@ p4bloch si desea capturar resultados de una serie de paréntesis de captura, debe usar el rangeAtIndex(index) de NSTextCheckingResult , en lugar de range . Aquí está el método de @MartinR para Swift2 desde arriba, adaptado para paréntesis de captura. En la matriz que se devuelve, el primer resultado [0] es la captura completa, y luego los grupos de captura individuales comienzan desde [1] . Comenté la operación del map (por lo que es más fácil ver lo que cambié) y lo reemplacé por bucles anidados.

func matches(for regex: String!, in 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)) var match = [String]() for result in results { for i in 0..<result.numberOfRanges { match.append(nsString.substringWithRange( result.rangeAtIndex(i) )) } } return match //return results.map { nsString.substringWithRange( $0.range )} //rangeAtIndex(0) } catch let error as NSError { print("invalid regex: /(error.localizedDescription)") return [] } }

Un ejemplo de uso podría ser, digamos que desea dividir una cadena del title year del title year por ejemplo, "Finding Dory 2016", podría hacer esto:

print ( matches(for: "^(.+)//s(//d{4})" , in: "Finding Dory 2016")) // ["Finding Dory 2016", "Finding Dory", "2016"]


Así es como lo hice, espero que traiga una nueva perspectiva de cómo funciona esto en Swift.

En este ejemplo a continuación, obtendré cualquier cadena entre []

var sample = "this is an [hello] amazing [world]" var regex = NSRegularExpression(pattern: "//[.+?//]" , options: NSRegularExpressionOptions.CaseInsensitive , error: nil) var matches = regex?.matchesInString(sample, options: nil , range: NSMakeRange(0, countElements(sample))) as Array<NSTextCheckingResult> for match in matches { let r = (sample as NSString).substringWithRange(match.range)//cast to NSString is required to match range format. println("found= /(r)") }


Descubrí que la solución de la respuesta aceptada desafortunadamente no se compila en Swift 3 para Linux. Aquí hay una versión modificada, entonces, que sí:

import Foundation func matches(for regex: String, in text: String) -> [String] { do { let regex = try RegularExpression(pattern: regex, options: []) let nsString = NSString(string: text) let results = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsString.length)) return results.map { nsString.substring(with: $0.range) } } catch let error { print("invalid regex: /(error.localizedDescription)") return [] } }

Las principales diferencias son:

  1. Swift en Linux parece requerir que se suelte el prefijo NS en los objetos Foundation para los que no existe un equivalente nativo de Swift. (Ver la propuesta de evolución rápida # 86. )

  2. Swift en Linux también requiere especificar los argumentos de options tanto para la inicialización de RegularExpression como para el método de matches .

  3. Por alguna razón, coaccionar un String en un NSString no funciona en Swift en Linux, sino inicializar un nuevo NSString con un String ya que la fuente sí funciona.

Esta versión también funciona con Swift 3 en macOS / Xcode con la única excepción de que debe usar el nombre NSRegularExpression lugar de RegularExpression .


Esta es una solución muy simple que devuelve una matriz de cadenas con las coincidencias.

Swift 3.

internal func stringsMatching(regularExpressionPattern: String, options: NSRegularExpression.Options = []) -> [String] { guard let regex = try? NSRegularExpression(pattern: regularExpressionPattern, options: options) else { return [] } let nsString = self as NSString let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length)) return results.map { nsString.substring(with: $0.range) } }


Incluso si el método matchesInString() toma una String como primer argumento, funciona internamente con NSString , y el parámetro de rango debe proporcionarse usando la longitud NSString y no como la longitud de la cadena Swift. De lo contrario, fallará para "grupos de grafemas extendidos" como "banderas".

A partir de Swift 4 (Xcode 9), la biblioteca estándar de Swift proporciona funciones para convertir entre Range<String.Index> y 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 [] } }

Ejemplo:

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

Nota: ¡ El Range($0.range, in: text)! desenvolvimiento forzado Range($0.range, in: text)! es seguro porque NSRange refiere a una subcadena del text cadena dado. Sin embargo, si desea evitarlo, use

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

en lugar.

(Respuesta anterior para Swift 3 y anteriores :)

Por lo tanto, debe convertir la cadena Swift dada en una NSString y luego extraer los rangos. El resultado se convertirá en una matriz de cadenas Swift automáticamente.

(El código para Swift 1.2 se puede encontrar en el historial de edición).

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 [] } }

Ejemplo:

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 [] } }

Ejemplo:

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


La mayoría de las soluciones anteriores solo dan la coincidencia completa como resultado ignorando los grupos de captura, por ejemplo: ^ / d + / s + (/ d +)

Para obtener las coincidencias del grupo de captura como se esperaba, necesita algo como (Swift4):

public extension String { 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 } }


Mi respuesta se basa en las respuestas dadas, pero hace que la coincidencia de expresiones regulares sea más sólida al agregar soporte adicional:

  • No solo devuelve coincidencias, sino que también devuelve todos los grupos de captura para cada coincidencia (ver ejemplos a continuación)
  • En lugar de devolver una matriz vacía, esta solución admite coincidencias opcionales
  • Evita do/catch al no imprimir en la consola y hace uso de la construcción de guard
  • Agrega matchingStrings como una extensión a String

Swift 4.2

//: Playground - noun: a place where people can play import Foundation extension String { func matchingStrings(regex: String) -> [[String]] { guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] } let nsString = self as NSString let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length)) return results.map { result in (0..<result.numberOfRanges).map { result.range(at: $0).location != NSNotFound ? nsString.substring(with: result.range(at: $0)) : "" } } } } "prefix12 aaa3 prefix45".matchingStrings(regex: "fix([0-9])([0-9])") // Prints: [["fix12", "1", "2"], ["fix45", "4", "5"]] "prefix12".matchingStrings(regex: "(?:prefix)?([0-9]+)") // Prints: [["prefix12", "12"]] "12".matchingStrings(regex: "(?:prefix)?([0-9]+)") // Prints: [["12", "12"]], other answers return an empty array here // Safely accessing the capture of the first match (if any): let number = "prefix12suffix".matchingStrings(regex: "fix([0-9]+)su").first?[1] // Prints: Optional("12")

Swift 3

//: Playground - noun: a place where people can play import Foundation extension String { func matchingStrings(regex: String) -> [[String]] { guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] } let nsString = self as NSString let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length)) return results.map { result in (0..<result.numberOfRanges).map { result.rangeAt($0).location != NSNotFound ? nsString.substring(with: result.rangeAt($0)) : "" } } } } "prefix12 aaa3 prefix45".matchingStrings(regex: "fix([0-9])([0-9])") // Prints: [["fix12", "1", "2"], ["fix45", "4", "5"]] "prefix12".matchingStrings(regex: "(?:prefix)?([0-9]+)") // Prints: [["prefix12", "12"]] "12".matchingStrings(regex: "(?:prefix)?([0-9]+)") // Prints: [["12", "12"]], other answers return an empty array here // Safely accessing the capture of the first match (if any): let number = "prefix12suffix".matchingStrings(regex: "fix([0-9]+)su").first?[1] // Prints: Optional("12")

Swift 2

extension String { func matchingStrings(regex: String) -> [[String]] { guard let regex = try? NSRegularExpression(pattern: regex, options: []) else { return [] } let nsString = self as NSString let results = regex.matchesInString(self, options: [], range: NSMakeRange(0, nsString.length)) return results.map { result in (0..<result.numberOfRanges).map { result.rangeAtIndex($0).location != NSNotFound ? nsString.substringWithRange(result.rangeAtIndex($0)) : "" } } } }


Muchas gracias a su answer por capturar grupos y partidos completos con Swift 4 , lo que me ayudó mucho. También hice una adición para las personas que quieren una respuesta error.localizedDescription cuando su expresión regular no es válida:

extension String { func matchingStrings(regex: String) -> [[String]] { do { let regex = try NSRegularExpression(pattern: regex) let nsString = self as NSString let results = regex.matches(in: self, options: [], range: NSMakeRange(0, nsString.length)) return results.map { result in (0..<result.numberOfRanges).map { result.range(at: $0).location != NSNotFound ? nsString.substring(with: result.range(at: $0)) : "" } } } catch let error { print("invalid regex: /(error.localizedDescription)") return [] } } }

Para mí, tener la Descripción localizada como error me ayudó a comprender qué salió mal al escapar, ya que muestra qué final regex swift intenta implementar.


Si desea extraer subcadenas de una Cadena, no solo la posición (sino la Cadena real, incluidos los emojis). Entonces, lo siguiente quizás sea una solución más simple.

extension String { func regex (pattern: String) -> [String] { do { let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions(rawValue: 0)) let nsstr = self as NSString let all = NSRange(location: 0, length: nsstr.length) var matches : [String] = [String]() regex.enumerateMatchesInString(self, options: NSMatchingOptions(rawValue: 0), range: all) { (result : NSTextCheckingResult?, _, _) in if let r = result { let result = nsstr.substringWithRange(r.range) as String matches.append(result) } } return matches } catch { return [String]() } } }

Ejemplo de uso:

"someText ๐Ÿ‘ฟ๐Ÿ…๐Ÿ‘ฟโšฝ๏ธ pig".regex("๐Ÿ‘ฟโšฝ๏ธ")

Devolverá lo siguiente:

["๐Ÿ‘ฟโšฝ๏ธ"]

Nota usando "/ w +" puede producir un inesperado ""

"someText ๐Ÿ‘ฟ๐Ÿ…๐Ÿ‘ฟโšฝ๏ธ pig".regex("//w+")

Devolverá esta matriz de cadenas

["someText", "๏ธ", "pig"]


Swift 4 sin NSString.

extension String { func matches(regex: String) -> [String] { guard let regex = try? NSRegularExpression(pattern: regex, options: [.caseInsensitive]) else { return [] } let matches = regex.matches(in: self, options: [], range: NSMakeRange(0, self.count)) return matches.map { match in return String(self[Range(match.range, in: self)!]) } } }