Objective-Cのmwaterfall/MWFeedParserというRSSフィード解析ライブラリを使っていて不便だったことに、本文中の画像URLを得る方法がないというものがありました。幸いHTMLタグ付きの本文を持っていたのでその中からimgタグをもってきてやろうということでインターフェースを用意して実装しプルリクしておきました。

プルリクの件はさておき、Objective-Cの正規表現がRubyやPerlと比べるとそれはもう使いづらかったのでメモっておきます。何しろ普通の文字列抽出からして辛かったので。。。

正規表現を

NSRegularExpression

正規表現オブジェクトはNSRegularExpressionで作ります。

例えばこんなふうに書きます。

1NSString* pattern = @"(<img.*?src=\\\")(.*?)(\\\".*?>)";
2NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];

Rubyだとこうですね。

1regex = Regexp.new("(<img.*?src=\\\")(.*?)(\\\".*?>)")

マッチングする

マッチングにはfirstMatchInStringmatchesInStringを使います。

1NSTextCheckingResult *match= [regex firstMatchInString:self.content options:NSMatchingReportProgress range:NSMakeRange(0, self.content.length)];
1NSArray *matches = [regex matchesInString:self.content options:0 range:NSMakeRange(0, self.content.length)];

これがもうわけわかんなくて、NSTextCheckingResultという全く直感的ではない名前のオブジェクトが返ってきます。こいつはrangeAtIndexというのを持っていて、index=0を渡すとマッチ全体が、index=1などとすると括弧でグループにした部分マッチの文字列中のインデックスをRangeで返してくれます。

なのでマッチした文字列そのものを取り出す場合は元の文字列のsubstringWithRangeを使う必要があるわけです。

Rubyならそんなことはしなくてもいいですね。

 1regex = /ho(geho)/
 2str = "hogehoge"
 3m = str.match(regex)
 4m[0] # => "hogeho"
 5m[1] # => "geho"
 6```☄
 7# 追加したコード全文
 8
 9HTML文書から`img`タグの`src`を引っ張ってくるコードは次のようになりました。`self.content`にHTMLの本文が入っています。
10
11```objc
12- (NSArray *)images {
13    if (!self.content){
14        return nil;
15    }
16
17    NSMutableArray *results = [NSMutableArray new];
18    NSString* pattern = @"(<img.*?src=\\\")(.*?)(\\\".*?>)";
19
20    NSError* error = nil;
21    NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
22
23    if (error == nil){
24        NSArray *matches = [regex matchesInString:self.content options:0 range:NSMakeRange(0, self.content.length)];
25        for (NSTextCheckingResult *match in matches){
26            [results addObject:[self.content substringWithRange:[match rangeAtIndex:2]]];
27        }
28    }
29    return results;
30}
31
32@end

正規表現といえば

昔作ったこんなのがあります。

Regex Replace

それについて書いたポスト。

Rubyの正規表現でテキストの置換ができるWebサイト - ぴよログRubyの正規表現でテキストの置換ができるWebサイト - ぴよログ