Categories: swift

swiftで正規表現使って抽出と置換をする

swiftで正規表現使って抽出する方法とか調べてみたらなんか意外にスッキリした方法が見つからなかったので、

やりかたの備忘録てきなものです。

モダンを謳ってるswiftのことだから正規表現用の素敵メソッドみたいのあるんでしょ!

とか思ってたらありませんでした。。。

モダンっぽくできるような事ができるものを作ってる人もいるにはいました。

Swiftでコードを書き始めて10日程。日も浅いしあまり偉そうな事を言えないけど、個人的にSwiftは気に入っています。色々な言語の良いトコ取りをしたと言われているだけに、コードを書くと分かりますが、うまく言えないけど謎の既視感が...
SwiftでJavascriptの正規表現リテラルっぽいものを実装してみた。 - Qiita - Qiita

いやはやありがたやありがたや。

ですが、ライブラリっぽいのをいれるのはなんか重くなったりするかなぁとか思ったりもしたので

標準のやつだけで正規表現できないかと調べてみました。

抽出編

調べてみると案外昔馴染みの方法でやるしかないかなぁといった印象だったので、

昔のコードをswiftに置き換えてみました。

iOS での正規表現をつかった文字列処理、Ruby や Perl はおろか、C# なんぞに比べても一段とわかりにくい気がする。どうしてこうなった。 Ruby で考える buf には次のような文字列が入っているとする。ファイルをごっそり読みこんだ状態で、改行も含まれている状態になっている。 1324650815.dat::【大阪】 ほげほげ 1324392193.dat::【電力】 ふにふに 1324640842.dat::【国際】 もこもこ 1324650659.dat::【社会】 どきどき ... このとき、'xxxxx.dat' というデータの部分の一覧を取り出したいとき、Ruby なら下…
iOS アプリ開発での正規表現を使った文字列処理がややこしい - 入隠者日記の移行先 - 入隠者日記の移行先
// NSString *body; に文字列が入っているものとする。
NSError *error = nil;
NSRegularExpression *regexp =
[NSRegularExpression regularExpressionWithPattern:@"(\\d+.dat)::"
options:0 error:&amp;error]; </code>

NSMutableArray *dats = [[NSMutableArray alloc] init];

id proc = ^(NSTextCheckingResult *arr, NSMatchingFlags flag, BOOL *stop) {
[dats addObject: [body substringWithRange:[arr rangeAtIndex:1]]];
};

<code> [regexp enumerateMatchesInString:body options:0
range:NSMakeRange(0, body.length) usingBlock:proc];

先人の知恵を借りつつ今回は

[test|hoge|hoge123] [test|huge|huge456]

という文字列から、hoge、hoge123, huge, huge456を抽出するようなコードを書きました。

let pattern = "\\[test\\|([a-z0-9]+)\\|([a-z0-9]+)\\]"
let content:NSString = "[test|hoge|hoge123] [test|huge|huge456]"
let regex = NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions.CaseInsensitive, error: nil)
regex?.enumerateMatchesInString(content, options: nil, range: NSMakeRange(0, content.length),
    usingBlock: {(result: NSTextCheckingResult!, flags: NSMatchingFlags, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
        for i in 0...result.numberOfRanges - 1 {
            let range = result.rangeAtIndex(i)
            println(content.substringWithRange(range))
        }
})

出力結果は以下の感じ

[test|hoge|hoge123]
hoge
hoge123
[test|huge|huge456]
huge
huge456

コツは、検索する対象をNSStringにしないといけないところ。

let content:NSString = "[test|hoge|hoge123] [test|huge|huge456]"

result.rangeAtIndexの返り値がNSRangeでかえってくるんだけど、StringのsubstringWithRangeはRangeを求めてくるんす。

なんだかイマイチ行けてない感はしますが、求めてくるんだからしょうがない。

それともStringでやる方法が別にあるのかもしれないですが、、、

誰かエロイ人知ってたら教えて下さい。

追記:

おまけに書いてる本を書いた神からStringでもかけるよとの導きを頂きました。

let pattern = "\\[test\\|([a-z0-9]+)\\|([a-z0-9]+)\\]"
let content = "[test|hoge|hoge123] [test|huge|huge456]"
let regex = NSRegularExpression(pattern: pattern, options: NSRegularExpressionOptions.CaseInsensitive, error: nil)
regex?.enumerateMatchesInString(content, options: nil, range: NSMakeRange(0,  countElements(content)),
    usingBlock: {(result: NSTextCheckingResult!, flags: NSMatchingFlags, stop: UnsafeMutablePointer<ObjCBool>) -> Void in
        for i in 0...result.numberOfRanges - 1 {
            let range = result.rangeAtIndex(i)
            let startIndex:String.Index = advance(content.startIndex, range.location)
            let endIndex = advance(content.startIndex, NSMaxRange(range))
            println(content[startIndex..<endIndex])
        }
})

だがしかしコードが長くなるのでObj-cの力を借りたくないというこだわりがない限りはNSStringのが楽そうです。

ちなみにStringにした場合はcontent.lengthがないので代わりにcountElements(content)を使ってます。

置換編

正規表現して取ってきた文字列を置換したいって事が多いですよね。

置換は案外簡単にさくっといけちゃいます。

抽出した結果をforとかで回さなくていいんですよ奥さん!!

今回は[test|hoge|hoge123] と[]に囲まれてるところをtest:hoge:hoge123とかに置換してみます。

let pattern = "\\[test\\|([a-z0-9]+)\\|([a-z0-9]+)\\]"
let content2 = "[test|hoge|hoge123] [test|huge|huge456]"
let replace = "test:$1:$2"
let replaceString = content2.stringByReplacingOccurrencesOfString(pattern, withString: replace, options: NSStringCompareOptions.RegularExpressionSearch, range: nil)
println(replaceString)

出力結果

test:hoge:hoge123 test:huge:huge456

今度は普通にStringでいけちゃいます。

コツは

let replace = "test:$1:$2"

$1とかで正規表現でマッチした結果を呼び出せます。

おわり

以外に調べたら正規表現苦戦しちゃいました。

正規表現はコードのほうがいつも考えるの苦戦しますが、書き方に苦戦しちゃいました。。

誰かの助けになればいいとおもいます。

おまけ

ちなみに弊社の神が書かれたsiwft本売ってます。

開発のプロが教える Swift標準ガイドブック

こちらにはswitchで正規表現を簡単に出来る方法なども載っているので気になる方は是非購入してみてください。

おまけ2

正規表現使うときは僕はここにお世話になってます。

Ruby-based regular expression editor/tester
Rubular - rubular.com
mogmet

View Comments

Share
Published by
mogmet