स्विफ्ट एक्सट्रैक्ट रेगेक्स मैच


175

मैं एक स्ट्रिंग से सब्सट्रिंग निकालना चाहता हूं जो रेगेक्स पैटर्न से मेल खाता है।

तो मैं कुछ इस तरह की तलाश में हूँ:

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

तो यह मेरे पास है:

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 ...
}

समस्या यह है, कि matchesInStringमुझे एक सरणी देता है NSTextCheckingResult, जहां NSTextCheckingResult.rangeप्रकार है NSRange

NSRangeके साथ असंगत है Range<String.Index>, इसलिए यह मुझे उपयोग करने से रोकता हैtext.substringWithRange(...)

किसी भी विचार कैसे कोड के कई लाइनों के बिना तेजी से इस साधारण बात को प्राप्त करने के लिए?

जवाबों:


313

यहां तक ​​कि अगर matchesInString()विधि Stringपहले तर्क के रूप में लेती है , तो यह आंतरिक रूप से काम करती है NSString, और रेंज पैरामीटर को NSStringलंबाई का उपयोग करके दिया जाना चाहिए और स्विफ्ट स्ट्रिंग लंबाई के रूप में नहीं। अन्यथा यह "विस्तारित अंगूर के समूहों" जैसे "झंडे" के लिए विफल हो जाएगा।

के रूप में स्विफ्ट 4 (Xcode 9), स्विफ्ट मानक पुस्तकालय कार्यों के बीच परिवर्तित करने के लिए प्रदान करता है Range<String.Index> और 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 []
    }
}

उदाहरण:

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

नोट: जबरन जारी किया गया स्क्रैप Range($0.range, in: text)!सुरक्षित है क्योंकि NSRangeदिए गए स्ट्रिंग के एक विकल्प को संदर्भित करता है text। हालाँकि, अगर आप इससे बचना चाहते हैं तो उपयोग करें

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

बजाय।


(स्विफ्ट 3 और इससे पहले का पुराना उत्तर :)

तो आपको दिए गए स्विफ्ट स्ट्रिंग को एक में बदलना चाहिए NSStringऔर फिर पर्वतमाला को निकालना चाहिए। परिणाम स्वचालित रूप से एक स्विफ्ट स्ट्रिंग सरणी में परिवर्तित हो जाएगा।

(स्विफ्ट 1.2 का कोड संपादन इतिहास में पाया जा सकता है।)

स्विफ्ट 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 []
    }
}

उदाहरण:

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

स्विफ्ट 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 []
    }
}

उदाहरण:

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

9
आपने मुझे पागल बनने से बचाया। मजाक नहीं कर रहा हूं। आपको बहुत - बहुत धन्यवाद!
मिचमैन २०'१५ को

1
@MathijsSegers: मैंने स्विफ्ट 1.2 / Xcode 6.3 के लिए कोड अपडेट किया है। मुझे बताने के लिए धन्यवाद!
मार्टिन आर

1
लेकिन क्या होगा अगर मैं एक टैग के बीच तार की खोज करना चाहता हूं? मुझे उसी परिणाम (मैच की जानकारी) की आवश्यकता है जैसे: regex101.com/r/cU6jX8/2 । आप किस रेगेक्स पैटर्न का सुझाव देंगे?
पीटर क्रेजिन

अद्यतन स्विफ्ट 1.2 के लिए, स्विफ्ट 2. नहीं कोड स्विफ्ट 2. साथ संकलन नहीं करता है
PatrickNLT

1
धन्यवाद! क्या होगा यदि आप केवल regex में वास्तव में () के बीच क्या निकालना चाहते हैं? उदाहरण के लिए, "[0-9] {3} ([0-9] {6})" मैं केवल अंतिम 6 नंबर प्राप्त करना चाहता हूं।
p4bloch

64

मेरा उत्तर दिए गए उत्तरों में से शीर्ष पर बनता है, लेकिन अतिरिक्त समर्थन जोड़कर रेगेक्स को अधिक मजबूत बनाता है:

  • केवल मैच ही नहीं बल्कि रिटर्न भी प्रत्येक मैच के लिए सभी कैप्चरिंग ग्रुप्स (नीचे उदाहरण देखें)
  • खाली सरणी पर लौटने के बजाय, यह समाधान वैकल्पिक मैचों का समर्थन करता है
  • do/catchकंसोल के लिए मुद्रण नहीं करने से बचता है और निर्माण का उपयोग करता हैguard
  • के विस्तार केmatchingStrings रूप में जोड़ता हैString

स्विफ्ट 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")

स्विफ्ट 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")

स्विफ्ट 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))
                    : ""
            }
        }
    }
}

1
कब्जा समूहों के बारे में अच्छा विचार है। लेकिन "गार्ड" स्विफ्टियर "डू / कैच" से क्यों है ??
मार्टिन आर

मैं ऐसे लोगों के साथ सहमत हूँ जैसे कि nshipster.com/guard-and-defer जो कहते हैं कि स्विफ्ट 2.0 निश्चित रूप से बयानों की बजाय नेस्टेड होने की बजाय जल्दी वापसी [...] की शैली को प्रोत्साहित करती प्रतीत होती है । नेस्टेड डू / स्टेटमेंट्स IMHO के लिए भी यही सही है।
लार्स ब्लमबर्ग

कोशिश / पकड़ स्विफ्ट में मूल त्रुटि हैंडलिंग है। try?उपयोग किया जा सकता है यदि आप केवल कॉल के परिणाम में रुचि रखते हैं, एक संभावित त्रुटि संदेश में नहीं। तो हाँ, guard try? ..यह ठीक है, लेकिन यदि आप त्रुटि प्रिंट करना चाहते हैं तो आपको एक डॉक-ब्लॉक की आवश्यकता है। दोनों तरीके स्विफ्टी हैं।
मार्टिन आर

3
मैंने आपके अच्छे स्निपेट, gist.github.com/neoneye/03cbb26778539ba5eb609d16200e4522
neoneye

1
जब तक मैंने यह नहीं देखा, मैं @MartinR जवाब के आधार पर अपना खुद का लिखने वाला था। धन्यवाद!
'22:

13

यदि आप एक स्ट्रिंग से सबस्ट्रिंग निकालना चाहते हैं, न केवल स्थिति, (लेकिन इमोजी सहित वास्तविक स्ट्रिंग)। फिर, निम्नलिखित शायद एक सरल समाधान है।

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]()
    }
  }
} 

उदाहरण उपयोग:

"someText 👿🏅👿⚽️ pig".regex("👿⚽️")

निम्नलिखित वापस करेंगे:

["👿⚽️"]

"\ W +" का उपयोग करके नोट एक अप्रत्याशित "" उत्पादन कर सकता है

"someText 👿🏅👿⚽️ pig".regex("\\w+")

इस स्ट्रिंग सरणी को वापस करेगा

["someText", "️", "pig"]

1
यह वही है जो मैं चाहता था
काइल किम

1
अच्छा! इसे स्विफ्ट 3 के लिए थोड़ा समायोजन की आवश्यकता है, लेकिन यह बहुत अच्छा है।
जेल डे

@ येल क्या समायोजन की जरूरत है? मैं तेजी से 5.1.3 का उपयोग कर रहा हूं
पीटर शोर्न

9

मैंने पाया कि स्वीकृत उत्तर का समाधान दुर्भाग्य से लिनक्स के लिए स्विफ्ट 3 पर संकलित नहीं है। यहाँ एक संशोधित संस्करण है, जो यह करता है:

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

मुख्य अंतर हैं:

  1. लिनक्स पर स्विफ्ट को NSफाउंडेशन ऑब्जेक्ट्स पर उपसर्ग छोड़ने की आवश्यकता होती है , जिसके लिए स्विफ्ट-मूल समकक्ष नहीं है। ( स्विफ्ट विकास प्रस्ताव # 86 देखें ।)

  2. लिनक्स पर स्विफ्ट को भी आरंभीकरण और विधि optionsदोनों के लिए तर्कों को निर्दिष्ट करने की आवश्यकता होती है ।RegularExpressionmatches

  3. किसी कारण से, मजबूर एक Stringएक में NSStringलिनक्स पर स्विफ्ट में काम नहीं करता लेकिन एक नए आरंभ NSStringएक साथ Stringस्रोत के रूप में काम करता है।

यह संस्करण मैकओएस / एक्सकोड पर स्विफ्ट 3 के साथ एकमात्र अपवाद के साथ भी काम करता है जिसे आपको NSRegularExpressionइसके बजाय नाम का उपयोग करना होगा RegularExpression


5

@ p4bloch यदि आप कैप्चर कोष्ठक की एक श्रृंखला से परिणाम कैप्चर करना चाहते हैं, तो आपको इसके बजाय की rangeAtIndex(index)विधि का उपयोग करने की आवश्यकता है । ऊपर से Swift2 के लिए @MartinR का तरीका है, कैप्चर कोष्ठक के लिए अनुकूलित। वापस आए हुए सरणी में, पहला परिणाम संपूर्ण कैप्चर है, और फिर व्यक्तिगत कैप्चर समूह शुरू होता है । मैंने ऑपरेशन की टिप्पणी की (इसलिए यह देखना आसान है कि मैंने क्या बदला) और इसे नेस्टेड लूप से बदल दिया।NSTextCheckingResultrange[0][1]map

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

एक उदाहरण का उपयोग मामला हो सकता है, कहते हैं कि आप title year"डोरि 2016 ढूँढना" जैसे एक स्ट्रिंग को विभाजित करना चाहते हैं।

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

इस जवाब ने मेरा दिन बना दिया। मैंने 2 घंटे बिताए एक समाधान की तलाश में जो समूहों के अतिरिक्त कैप्चर के साथ रेगुलर अभिव्यक्ति को संतुष्ट कर सके।
अहमद

यह काम करता है लेकिन अगर कोई सीमा नहीं मिली तो यह दुर्घटनाग्रस्त हो जाएगा। मैंने इस कोड को संशोधित किया ताकि फ़ंक्शन वापस आए [String?]और for i in 0..<result.numberOfRangesब्लॉक में, आपको एक परीक्षण जोड़ना होगा जो केवल मैच को जोड़ देता है यदि रेंज! = NSNotFound, अन्यथा इसे शून्य जोड़ना चाहिए। देखें: stackoverflow.com/a/31892241/2805570
stef

4

NSString के बिना स्विफ्ट 4।

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)!])
        }
    }
}

इसके बाद के संस्करण समाधान के साथ सावधान रहें: NSMakeRange(0, self.count)क्योंकि, सही नहीं है selfएक है String(= UTF8) और नहीं एक NSString(= UTF16)। तो self.countयह जरूरी नहीं है कि nsString.length(जैसा कि अन्य समाधानों में उपयोग किया जाता है)। आप के साथ सीमा गणना की जगह ले सकताNSRange(self.startIndex..., in: self)
pd95

3

ऊपर दिए गए अधिकांश समाधान केवल पूर्ण मिलान को कैप्चर समूहों की अनदेखी के परिणामस्वरूप देते हैं जैसे: ^ \ d + \ s + (\ d +)

उम्मीद के अनुसार कैप्चर ग्रुप मैच पाने के लिए आपको कुछ चाहिए (स्विफ्ट 4):

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

यह बहुत अच्छा है अगर आप सिर्फ पहला परिणाम चाहते हैं, प्रत्येक परिणाम के लिए इसे प्राप्त करने की आवश्यकता for index in 0..<matches.count {हैlet lastRange... results.append(matchedString)}
Geoff

क्‍लॉज के लिए इस तरह दिखना चाहिए:for i in 1...lastRangeIndex { let capturedGroupIndex = match.range(at: i) if capturedGroupIndex.location != NSNotFound { let matchedString = (self as NSString).substring(with: capturedGroupIndex) results.append(matchedString.trimmingCharacters(in: .whitespaces)) } }
CRE8IT

2

यह मैंने कैसे किया, मुझे आशा है कि यह एक नया परिप्रेक्ष्य लाएगा कि स्विफ्ट पर यह कैसे काम करता है।

नीचे दिए गए इस उदाहरण में मुझे किसी भी स्ट्रिंग के बीच मिलेगा []

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)")
}

2

यह एक बहुत ही सरल समाधान है जो माचिस की तीली के साथ सरणी देता है

स्विफ्ट 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)
        }
    }

2

सभी मैचों को वापस करने और स्विफ्ट 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)) }
        } ?? []
    }
}

स्ट्रिंग्स का 2-डायमेंशनल सरणी लौटाता है:

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

रिटर्न ...

[["fix12su", "12"], ["fix1su", "1"]]

// First element of sub-array is the match
// All subsequent elements are the capture groups

0

लार्स ब्लमबर्ग के लिए बड़ा धन्यवाद कि उन्होंने स्विफ्ट 4 के साथ समूहों और पूर्ण मैचों पर कब्जा करने के लिए अपना जवाब दिया , जिससे मुझे काफी मदद मिली। मैंने उन लोगों के लिए भी एक जोड़ बनाया, जो एक त्रुटि चाहते हैं। जब उनके regex को अमान्य माना जाता है, तो प्रतिक्रिया दें।

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

मेरे लिए स्थानीयकृतकरण को त्रुटि के रूप में समझने में मदद मिली कि भागने में क्या गलत था, क्योंकि यह प्रदर्शित करता है कि अंतिम रेग्क्स स्विफ्ट को लागू करने की कोशिश करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.