1. ホーム
  2. アイオス

[解決済み】Swiftで正規表現にマッチしたものを取り出す

2022-04-11 05:08:32

質問

文字列から正規表現にマッチする部分文字列を抽出したい。

そこで、次のようなものを探しています。

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(...)

この単純なことを、何行もコードを書くことなく、swift で実現する方法をご存知ですか?

どのように解決するのですか?

たとえ matchesInString() メソッドは String を第一引数にとります。 で内部的に動作します。 NSString を指定する必要があり、range パラメータを指定する必要があります。 を使用して NSString の長さではなく、Swift の文字列の長さとして扱われます。そうでない場合は フラグのような拡張書記素クラスタでは失敗します。

現在 スウィフト4 (Xcode 9) では、Swift 標準の との間で変換する関数を提供しています。 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]) }
        }

の代わりに


(Swift 3 以前の古い回答です)

そこで、与えられたSwiftの文字列を NSString を抽出し、さらに の範囲を指定します。結果は自動的にSwiftの文字列配列に変換されます。

(Swift 1.2用のコードは編集履歴にあります)

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

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

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