もっと良い方法がありました!
普通に標準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の総和を取って、開始地点から引き算することでも実現出来ますが、処理が重いだけでメリットはないでしょう。
簡単ですが、以上です。