Objective-Cでの正規表現によるマッチ文字列取り出し
移転しました →
Objective-Cのmwaterfall/MWFeedParserというRSSフィード解析ライブラリを使っていて不便だったことに、本文中の画像URLを得る方法がないというものがありました。幸いHTMLタグ付きの本文を持っていたのでその中からimg
タグをもってきてやろうということでインターフェースを用意して実装しプルリクしておきました。
プルリクの件はさておき、Objective-Cの正規表現がRubyやPerlと比べるとそれはもう使いづらかったのでメモっておきます。何しろ普通の文字列抽出からして辛かったので。。。
正規表現を
NSRegularExpression
正規表現オブジェクトはNSRegularExpression
で作ります。
例えばこんなふうに書きます。
NSString* pattern = @"(<img.*?src=\\\")(.*?)(\\\".*?>)"; NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
Rubyだとこうですね。
regex = Regexp.new("(<img.*?src=\\\")(.*?)(\\\".*?>)")
マッチングする
マッチングにはfirstMatchInString
やmatchesInString
を使います。
NSTextCheckingResult *match= [regex firstMatchInString:self.content options:NSMatchingReportProgress range:NSMakeRange(0, self.content.length)];
NSArray *matches = [regex matchesInString:self.content options:0 range:NSMakeRange(0, self.content.length)];
これがもうわけわかんなくて、NSTextCheckingResult
という全く直感的ではない名前のオブジェクトが返ってきます。こいつはrangeAtIndex
というのを持っていて、index=0
を渡すとマッチ全体が、index=1
などとすると括弧でグループにした部分マッチの文字列中のインデックスをRangeで返してくれます。
なのでマッチした文字列そのものを取り出す場合は元の文字列のsubstringWithRange
を使う必要があるわけです。
Rubyならそんなことはしなくてもいいですね。
regex = /ho(geho)/ str = "hogehoge" m = str.match(regex) m[0] # => "hogeho" m[1] # => "geho"
追加したコード全文
HTML文書からimg
タグのsrc
を引っ張ってくるコードは次のようになりました。self.content
にHTMLの本文が入っています。
- (NSArray *)images { if (!self.content){ return nil; } NSMutableArray *results = [NSMutableArray new]; NSString* pattern = @"(<img.*?src=\\\")(.*?)(\\\".*?>)"; NSError* error = nil; NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error]; if (error == nil){ NSArray *matches = [regex matchesInString:self.content options:0 range:NSMakeRange(0, self.content.length)]; for (NSTextCheckingResult *match in matches){ [results addObject:[self.content substringWithRange:[match rangeAtIndex:2]]]; } } return results; } @end☄
正規表現といえば
昔作ったこんなのがあります。
それについて書いたポスト。