対象:
Swift4

正規表現による文字列の抽出(Swift)

正規表現を使って文字列中にある任意のパターンにマッチした部分文字列を抽出する。NSRegularExpressionを使うと、正規表現を用いた文字列のマッチ、及び抽出を行うことができる。

グループ機能にも対応しており、正規表現パターン中の()で括った部分文字列をキャプチャすることもできる。正規表現パターンにおける各グループは、firstMatchメソッドから返されるNSTextCheckingResultオブジェクトのrangeメソッドで取得できる。

検索対象の文字列から、マッチさせたグループ毎に文字列を抽出するためにNSStringのsubstringメソッドを使う。このため、予め検索対象の文字列をNSStringにキャストする等しておくと後で楽かも知れない。

        // 正規表現を使って文字列を抽出
        // NSRangeを扱うので検索対象の文字列は先にNSStringにしておくと後で楽かも
        let nsSentence: NSString = "Apple?は新しい言語Swiftを発表しました。"
        let regex = try? NSRegularExpression(pattern: "(?i)^(apple.{1})は.*(swift)を(.*)しました。$", options: NSRegularExpression.Options())
        if let result = regex?.firstMatch(in: nsSentence as String, options: NSRegularExpression.MatchingOptions(), range: NSMakeRange(0, nsSentence.length)) {
            // Swift 3の書き方
            // if let range = result.range.toRange() {
            // Swift 4の書き方
            if let range = Range(result.range) {
                print("range: \(range) string: \(nsSentence.substring(with: result.range))")
            }
            print(result.numberOfRanges)
            for i in 0..<result.numberOfRanges {
                // Swift 3の書き方
                // if let range = result.rangeAt(i).toRange() {
                // Swift 4の書き方
                if let range = Range(result.range(at: i)) {
                    // Swift 3の書き方
                    // print("range(\(i)): \(range) string: \(nsSentence.substring(with: result.rangeAt(i)))")
                    // Swift 4の書き方
                    print("range(\(i)): \(range) string: \(nsSentence.substring(with: result.range(at: i)))")
                }
            }
        } else {
            print("文字列は見つかりませんでした。")
        }

以下のように、rangeプロパティ、及びrange(at: 0)で得られる範囲の文字列はマッチした文字列全体、range(at: 1)以降で得られる範囲の文字列は各グループの文字列となる。上記の正規表現パターンで言えば、(apple.{1})はグループ1、(swift)はグループ2、そして(.*)はグループ3となる。

range: 0..<26 string: Apple?は新しい言語Swiftを発表しました。
range(0): 0..<26 string: Apple?は新しい言語Swiftを発表しました。
range(1): 0..<7 string: Apple?
range(2): 13..<18 string: Swift
range(3): 19..<21 string: 発表

NSRegularExpressionの正規表現パターンで(?i)フラグを使う代わりに、options引数でNSRegularExpression.Options.caseInsensitiveを渡すと、(?i)フラグと同じ効果が得られる。これはケースバイケースなので、好きなほうを使うと良いだろう。もちろん、大文字小文字を区別して(casesensitive)パターンにマッチさせたい場合はどちらも必要ない。

        // 正規表現パターンで(?i)フラグを使う代わりに、options引数でNSRegularExpression.Options.caseInsensitiveを指定
        let regex = try? NSRegularExpression(pattern: "^(apple.{1})は.*(swift)を(.*)しました。$", options: .caseInsensitive)

グループ機能を使えば、例えば日時を表す文字列から年、月、及び日だけを簡単に取り出すことができる。数字を表す正規表現として\d(バックスラッシュd)が使えるが、Swiftの場合、\(バックスラッシュ)は文字列の補間にも使われているため、\\d(バックスラッシュバックスラッシュd)と2つ重ねてエスケープする必要がある。4ケタの整数であれば(\\d{4})でキャプチャできる。

        let nsDateStr: NSString = "2015-09-09 08:43:59"
        let rd = try? NSRegularExpression(pattern: "^(\\d{4})-(\\d{2})-(\\d{2})", options: NSRegularExpression.Options())
        if let r = rd?.firstMatch(in: nsDateStr as String, options: NSRegularExpression.MatchingOptions(), range: NSMakeRange(0, nsDateStr.length)) {
            // Swift 3の書き方
            // print("西暦\(nsDateStr.substring(with: r.rangeAt(1)))年\(nsDateStr.substring(with: r.rangeAt(2)))月\(nsDateStr.substring(with: r.rangeAt(3)))日です。")
            // Swift 4の書き方
            print("西暦\(nsDateStr.substring(with: r.range(at: 1)))年\(nsDateStr.substring(with: r.range(at: 2)))月\(nsDateStr.substring(with: r.range(at: 3)))日です。")
        }
西暦2015年09月09日です。

単純にマッチさせたいだけなら、対象の文字列はNSStringでなくただのStringでも良いかも知れない。以下は文字列がiPhone 4s、5、5s、6、または6sの場合にiOS 9に対応していると表示する例である。

\s(バックスラッシュs)は空白文字にマッチする。また、*は0回以上の繰返しを意味するので、"iPhone"と"6s"の間のスペースはあってもなくてもマッチする。(?:)はキャプチャされないグループを意味し、候補の文字列がいくつかある場合に使える。

        let ios9re = try? NSRegularExpression(pattern: "^(?i)iPhone\\s*(?:4s|5|5s|6|6s)$", options: NSRegularExpression.Options())
        // 単純にマッチさせたいだけならStringで良いかも
        let phones = ["iPhone 4", "iPhone 4s", "iPhone 5", "IPHONE 5S", "iPhone  6", "iphone6s"]
        for phone in phones {
            // NSString.lengthはString.utf16.countで代用できそう
            if let _ = ios9re?.firstMatch(in: phone, options: NSRegularExpression.MatchingOptions(), range: NSMakeRange(0, phone.utf16.count)) {
                print("\(phone)はiOS 9に対応しています。")
            } else {
                print("\(phone)はiOS 9に対応していません。")
            }
        }
iPhone 4はiOS 9に対応していません。
iPhone 4sはiOS 9に対応しています。
iPhone 5はiOS 9に対応しています。
IPHONE 5SはiOS 9に対応しています。
iPhone  6はiOS 9に対応しています。
iphone6sはiOS 9に対応しています。

正直な印象としてあまり使いやすいとは感じないが、Swiftでもとりあえず正規表現を使用した文字列の検索・抽出が可能となっている。

(2015/02/20)
() Swift 4.0対応。

新着情報
【オープンソースソフトウェア環境構築】Apple silicon Macで開発環境を構築
【Rust Tips】Actix webでJSONをPOSTする
【Rust Tips】コマンドライン引数を取得する

Copyright© 2004-2019 モバイル開発系(K) All rights reserved.
[Home]