LoginSignup
15
15

More than 5 years have passed since last update.

NSStringで複数の正規表現マッチを置換する

Last updated at Posted at 2014-04-24

もっと良い方法がありました!

普通に標準SDKに含まれているメソッドで出来るようです。詳しくはコメント欄をご覧ください

正規表現(NSRegularExpression)や、NSDataDetectorによるマッチでは、NSTextCheckingResultのNSArrayが取得できます。

    NSString *pattern = @"[a-z0-9]{1,10}";
    NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern
                                                                           options:0
                                                                             error:&error];

NSArray * matches = [regex matchesInString:string options:0 range:NSMakeRange(0, string.length)];

こいつを普通にループして処理するのはバグを生むので、逆順に走査しましょうお話。

置換するなら

[matches enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSTextCheckingResult* match, NSUInteger idx, BOOL *stop) {
        [workString replaceCharactersInRange:match.range withString:string];
    }];

削除するなら

    [matches enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSTextCheckingResult* match, NSUInteger idx, BOOL *stop) {
        [workString deleteCharactersInRange:match.range];
    }];

なぜ普通にループするとバグるのか

削除のケースに関して言うと

NSString *string = @"abc|efg";

のとき、上記にある英数字にのみマッチする正規表現を使って削除しようとすると。

まず、各NSTextCheckingResultが持つrangeは以下の様になります。

  • (0,3)
  • (4,3)

一回目のループではabc|になりますね。
二回目のループでは、この中で (4,3)を置換しようとするので、Exceptionが発生してしまうわけです。
これは、後ろから走査すれば発生しない問題です。

勿論、毎回rangeのlengthの総和を取って、開始地点から引き算することでも実現出来ますが、処理が重いだけでメリットはないでしょう。

簡単ですが、以上です。

15
15
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
15