NSRangeの使い方

NSRangeとは?

NSRangeはlocation(位置)とlength(長さ)の2つを持つ、"範囲"を表す構造体です。
文字列の検索、分割、切り出し等に重宝します。
※2013/06/11 正規表現検索のサンプルを追加


例えば以下のようにrangeOfStringメソッドを使うと、
"列です"という文字列が最初に現れた「位置」と「長さ」をNSRangeという「範囲」で取得することができます。

NSString *text = @"テスト用の文字列です 2012/8/13";
NSRange rangeTest = [text rangeOfString:@"列です"];

取得できる範囲のイメージは以下です。



検索に失敗した時

検索した文字列が見つからない時は、NSNotFoundという値がlocationに返却されるので、
if(range.location == NSNotFound)のように条件に使えます。


パフォーマンスについて

以下メソッドでoptionにNSLiteralSearchを指定する事で、
検索のパフォーマンスが大分変わるようです。

rangeOfString:(NSString *)aString options:(NSStringCompareOptions)mask



オプションはOR演算で組み合わせ可能です。

NSStringCompareOptions options = NSLiteralSearch | NSCaseInsensitiveSearch;

尚、NSLiteralSearch指定のみなら早くなりますが、合成文字を無視する事、
組み合わせによりむしろ遅くなる事があるので注意が必要です。
NSRegularExpressionSearchと組み合わせたら3倍程時間かかってました。
参考:http://blog.fenrir-inc.com/jp/2013/04/stringfind-options.html


以下はサンプルコードです。ご参考までに。

        // NSRangeのテスト用の文字列を準備
        NSString *text = @"テスト用の文字列です 2012/8/13";
        // 画像と同様のNSRangeのテスト
        NSRange rangeTest = [text rangeOfString:@"列です"];
        NSLog(@"rangeTestのrangeは%@",NSStringFromRange(rangeTest));
        
        // 「12」という文字列が最初に現れる場所を検索し、位置と長さを持つNSRange構造体を取得する。
        NSRange range1 = [text rangeOfString:@"12"];
        NSLog(@"range1のlocationは%lu",range1.location);        
        
        // 検索した文字列が存在しない時、range.locationにはlongの最大値が返却される。
        NSRange range2 = [text rangeOfString:@"9"];
        NSLog(@"range2のlocationは%lu",range2.location);        
        
        // 検索対象が空文字だった場合も、range.locationにはlongの最大値が返却される。
        NSString *emptyText = @"";
        NSRange rangeEmpty = [emptyText rangeOfString:@"aaa"];
        NSLog(@"rangeEmptyのlocationは%lu",rangeEmpty.location);
        
        // 検索対象がnilだった場合は、0が返却されます。
        // Objective-Cではnilへのメッセージは無視されますが、
        // 戻り値があるメソッドは0が返却されます。
        NSString *nilText = nil;
        NSRange rangeNil = [nilText rangeOfString:@"aaa"];
        NSLog(@"rangeNilのlocationは%lu",rangeNil.location);        
        
        // [実行結果]
        // rangeTestのrangeは{7, 3}
        // range1のlocationは13                
        // range2のlocationは9223372036854775807        
        // rangeEmptyのlocationは9223372036854775807
        // rangeNilのlocationは0
        
        // 検索対象が空文字だった時、検索した文字列が存在しない場合に返却される値は、
        // long型の最大値LONG_MAXを定数化したNSIntegerMaxをenumで再定義したNSNotFoundなので、
        // 検索対象が見つからなかったらxxするという条件に、以下のようにNSNotFoundを利用することができる。
        if (range2.location == NSNotFound) {
            NSLog(@"rangeOfStringで検索対象が見つからなかった時の処理。");
        }
        // rangeを作りたい場合はNSMakeRangeを使う、例えば先頭から3文字の切り出し
        NSRange rangeZeroTo3 = NSMakeRange(0, 3);
        NSString *fst3Str = [text substringWithRange:rangeZeroTo3];
        NSLog(@"%@",fst3Str);
        // [実行結果]
        // テスト

        // 正規表現による検索、以下文字列から「2013年」を探す。
        // 変数を正規表現に含めたい時はstringWithFormatで%@を使う。
        NSString *searchTarget = @"先日行われた2013年のWWDCはiOS7の発表がありました。";
        NSString *suffixJapaneaseYear = @"年";
        NSString *regexStr = [NSString stringWithFormat:@"[0-9]{1,4}%@",suffixJapaneaseYear];
        NSRange  resultRange = [searchTarget rangeOfString:regexStr options:NSRegularExpressionSearch];
        NSLog(@"検索結果は%@です。",[searchTarget substringWithRange:resultRange]);
        // [実行結果]
        // 検索結果は2013年です。

        // 100万回の検索でNSLiteralSearchを指定
        NSLog(@"start");
        NSString *ret;
        NSString *searchTarget2 = @"先日行われた2013年のWWDCはiOS7の発表がありました。";
        NSString *japaneaseYear = @"年";
        for (int i = 0; i < 10000000; i++) {
            // 以下コメントを切り替えて比較
            NSRange  resultRange = [searchTarget2 rangeOfString:japaneaseYear];
            //NSRange  resultRange = [searchTarget rangeOfString:japaneaseYear options:NSLiteralSearch];
            ret = [searchTarget2 substringWithRange:resultRange];
        }
        NSLog(@"end 検索結果は%@です。",ret);



[参考サイト]
・文字列からある文字列が含まれる全てのNSRangeを取得する。(stackoverflow)
http://stackoverflow.com/questions/8533691/how-to-get-all-nsrange-of-a-particular-character-in-a-nsstring

・任意の文字が現れる場所を確認する
http://program.station.ez-net.jp/special/handbook/objective-c/nsstring/range-of-string.asp