対象: 正規表現による文字列の抽出(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対応。
Copyright© 2004-2019 モバイル開発系(K) All rights reserved.
[Home]
|