ObjecTips

Swift & Objective-C で iOS とか macOS とか

NSPredicate で正規表現

NSPredicate正規表現の組み合わせはちょっとややこしい。
例えば、正規表現で数字列を検出する場合 \d+ を用いる。
NSString でこの文字列を扱う場合 \ がエスケープ文字なので、これをこのまま NSString に入れると Unknown escape sequence '\d' というエラーが出て以下のような結果になる。

    NSLog(@"%@", @"\d+");
    // d+

NSString\d+ という文字列を表す場合はエスケープ文字であるバックスラッシュの直前にバックスラッシュを付けて、バックスラッシュ自体を文字として扱うようにする。

    NSLog(@"%@", @"\\d+");
    // \d+

例えば NSStringrangeOfString: メソッドでの正規表現を用いた検索は以下のようになる。

    NSRange range = [@"123" rangeOfString:@"\\d+"
                                  options:NSRegularExpressionSearch];
    NSLog(@"%@", NSStringFromRange(range));
    // {0, 3}


で、次に NSPredicate正規表現
NSPredicate の format 指定

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES '\\d+'"];
    NSLog(@"%@", predicate);
    // SELF MATCHES "d+"

とした場合、NSPredicate による format の parse 時にエスケープ文字が動作してしまい、 実際に NSPredicate によって検索が実行されるフォーマットは SELF MATCHES "d+" となってしまう。
これを正しく \\d+ として動作させるためにはバックスラッシュを重ねてもう一度エスケープさせてやる必要がある。
以下のようになる。

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES '\\\\d+'"];
    NSLog(@"%@", predicate);
    // SELF MATCHES "\\d+"


バックスラッシュ4つはさすがに分かりづらい。
実は format に %@ で文字列 @"\\d+" を突っ込んでやるとそのあたりよしなに処理してくれる(放り込んだ文字列をそのまま文字列として扱ってくれる)ので、以下のように書くのが簡単。

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", @"\\d+"];
    NSLog(@"%@", predicate);
    // SELF MATCHES "\\d+"

以下まとめのコード