NSString(4)
2010. 03. 30
和訳どころか英語でも詳しい解説が無いメソッドがあって難儀してますが、引き続きNSStringクラスの説明です。
●行と段落範囲の判別(Determining Line and Paragraph Ranges)
・– getLineStart:end:contentsEnd:forRange:
– (void)getLineStart:(NSUInteger *)startIndex end:(NSUInteger *)lineEndIndex contentsEnd:(NSUInteger *)contentsEndIndex forRange:(NSRange)aRange
指定した範囲の行の開始位置と終了位置(改行コード含む/含まない)を返します
改行を判別する記号(改行コード)は次の通りです
・U+000D:\r または CR(Carriage return:行頭への復帰。行送りは含まない)
・U+2028:Unicode line separator(行区切り記号)
・U+000A:\n または LF(Line Feed:行送り。行頭への復帰は含まない)
・U+2029:Unicode paragraph separator(段落区切り記号)
・ またはCRLF
startIndex:指定した範囲にある行の先頭文字位置を返します
必要が無い場合はNULLを指定します
lineEndIndex:指定した範囲にある行の改行コード含む終端文字位置を返します
必要が無い場合はNULLを指定します
contentsEndIndex:指定した範囲にある行の改行コード含まない終端文字位置を返します
必要が無い場合はNULLを指定します
aRange:検索する行のある範囲を指定します
この範囲は検索するNSStringオブジェクトより大きく設定してはいけません
超える範囲を設定した場合、NSRangeExceptionで例外を発生させます
文字位置は0から数え、改行コードは1文字として扱います。
サンプルコードの例文の文字位置は下表のようになります。
改行コードを跨がない範囲を指定(19文字目から2文字分、つまりYukihoの『ho』)すると、
LineStart=15, LineEnd=23, ContentsEnd=21
という結果になります。
開始位置はともかく、終端位置が一つずれているように感じますが、『その一つ手前までが範囲』という解釈のようです。
(その方が『終端位置から文字列を追加』などをしようとした際に指定し易いからと思われます)
実際、改行コードを2つ跨いだ範囲指定(10文字目から14文字分、ChihayaのhayaからYayoiのYまで)をすると、
LineStart=7, LineEnd=28, ContentsEnd=28
という結果になりますが、実際には28文字目は存在しません(28文字目を表示しようとするとエラーになります)。
・- lineRangeForRange:
– (NSRange)lineRangeForRange:(NSRange)aRange
指定した範囲にある行の全体範囲を返します(改行コードも含みます)
aRange:検索する行のある範囲を指定します
・– getParagraphStart:end:contentsEnd:forRange:
– (void)getParagraphStart:(NSUInteger *)startIndex end:(NSUInteger *)endIndex contentsEnd:(NSUInteger *)contentsEndIndex forRange:(NSRange)aRange
指定した範囲の段落の開始位置と終了位置(改行コード含む/含まない)を返します
段落を判別する記号(改行コード)は次の通りです
・U+000D:\rまたは CR(Carriage return:行頭への復帰。行送りは含まない)
・U+000A:\nまたは LF(Line Feed:行送り。行頭への復帰は含まない)
・U+2029:Unicode paragraph separator(段落区切り記号)
・ またはCRLF
– getLineStart:end:contentsEnd:forRange:との違いはUnicode line separator(行区切り記号:U+2028)を判別するかどうかです
getLine~ではUnicode line separatorにも反応し行末と判断しますが、getParagraph~は段落末と判断せずスルーします(getLine~はUnicode paragraph separatorも行末と判断します)
逆に言えば、本来段落末(Unicode paragraph separator)だけに反応してほしいgetLine~が、改行コードである 、\rや\nにも反応してしまいますので注意が必要です
startIndex:指定した範囲にある段落の先頭文字位置を返します
必要が無い場合はNULLを指定します
lineEndIndex:指定した範囲にある段落の改行コード含む終端文字位置を返します
必要が無い場合はNULLを指定します
contentsEndIndex:指定した範囲にある段落の改行コード含まない終端文字位置を返します
必要が無い場合はNULLを指定します
aRange:検索する段落のある範囲を指定します
この範囲は検索するNSStringオブジェクトより大きく設定してはいけません
超える範囲を設定した場合、NSRangeExceptionで例外を発生させます
– getLineStart:end:contentsEnd:forRange:のサンプル文字列の改行コードを、Unicode line separatorとUnicode paragraph separatorに置き換えました
改行コードを跨がない範囲を指定(2文字目から2文字分、つまりHarukaの『ru』)すると、
LineStart=0, LineEnd=15, ContentsEnd=14
という結果になります。
前述の通り、6文字目のUnicode line separatorを\rや\nにすると、
LineStart=0, LineEnd=7, ContentsEnd=6
になります
・– paragraphRangeForRange:
– (NSRange)paragraphRangeForRange:(NSRange)aRange
指定した範囲にある段落の全体範囲を返します(Unicode paragraph separatorも含みます)
aRange:検索する行のある範囲を指定します
●合成済文字範囲の判別(Determining Composed Character Sequences)
・– rangeOfComposedCharacterSequenceAtIndex:
– (NSRange)rangeOfComposedCharacterSequenceAtIndex:(NSUInteger)anIndex
指定した位置の文字範囲を判別して返します
Unicodeでは結合文字という、濁点や半濁点などを前の文字と合成して1文字として表示するものがあります。
(他にも色々ありますのでWikipedia/Unicodeの互換文字を参照してください)
このように結合文字の場合、表示上1文字でもコード上では複数文字で構成されている場合が有るため、その検出に使えます
anIndex:文字範囲を判別する文字位置を指定します
サンプルコードの文字列は下記のようになっています
7文字目は『゚(U+309A)』で、6文字目と合わせて表示上は『ぽ』となりますが、結果は
Location=6, Length=2, ゆきぽ
となります
ちなみに、rangeOfComposedCharacterSequenceAtIndex:2、NSMakeRange(0, 3)では、
Location=2, Length=1, ゆきぽ
という結果になります
・– rangeOfComposedCharacterSequencesForRange:
– (NSRange)rangeOfComposedCharacterSequencesForRange:(NSRange)range
指定した範囲の文字範囲を判別して返します
Unicodeでは結合文字という、濁点や半濁点などを前の文字と合成して1文字として表示するものがあります。
(他にも色々ありますのでWikipedia/Unicodeの互換文字を参照してください)
このように結合文字の場合、表示上1文字でもコード上では複数文字で構成されている場合が有るため、その検出に使えます
range:文字範囲を判別する範囲を指定します
この範囲は検索するNSStringオブジェクトより大きく設定してはいけません
範囲指定で4文字目から3文字分(ゆきほ)を指定すると、
Location=4, Length=4, ゆきぽ
となり、結合文字を認識して数えている事が分かります
参考文献
・NSString Class Reference
・Cocoa APIとか(iPhoneとか)/NSString
・Studying Cocoa/NSString
・Wikipedia/改行コード
・Predicate Programming Guide
・String Programming Guide for Cocoa
・合成文字対応
・Wikipedia/Unicodeの互換文字
●行と段落範囲の判別(Determining Line and Paragraph Ranges)
・– getLineStart:end:contentsEnd:forRange:
– (void)getLineStart:(NSUInteger *)startIndex end:(NSUInteger *)lineEndIndex contentsEnd:(NSUInteger *)contentsEndIndex forRange:(NSRange)aRange
指定した範囲の行の開始位置と終了位置(改行コード含む/含まない)を返します
改行を判別する記号(改行コード)は次の通りです
・U+000D:\r または CR(Carriage return:行頭への復帰。行送りは含まない)
・U+2028:Unicode line separator(行区切り記号)
・U+000A:\n または LF(Line Feed:行送り。行頭への復帰は含まない)
・U+2029:Unicode paragraph separator(段落区切り記号)
・ またはCRLF
startIndex:指定した範囲にある行の先頭文字位置を返します
必要が無い場合はNULLを指定します
lineEndIndex:指定した範囲にある行の改行コード含む終端文字位置を返します
必要が無い場合はNULLを指定します
contentsEndIndex:指定した範囲にある行の改行コード含まない終端文字位置を返します
必要が無い場合はNULLを指定します
aRange:検索する行のある範囲を指定します
この範囲は検索するNSStringオブジェクトより大きく設定してはいけません
超える範囲を設定した場合、NSRangeExceptionで例外を発生させます
unsigned int lineStart, lineEnd, contEnd;
NSString *オブジェクト1 = @"Haruka\rChihaya\nYukiho\r\nYayoi";
[オブジェクト1 getLineStart:&lineStart end:&lineEnd contentsEnd:&contEnd forRange:NSMakeRange(19, 2)] ;
NSString *オブジェクト2 = [NSString stringWithFormat:@"LineStart=%d, LineEnd=%d, ContentsEnd=%d", lineStart, lineEnd, contEnd];
NSString *オブジェクト1 = @"Haruka\rChihaya\nYukiho\r\nYayoi";
[オブジェクト1 getLineStart:&lineStart end:&lineEnd contentsEnd:&contEnd forRange:NSMakeRange(19, 2)] ;
NSString *オブジェクト2 = [NSString stringWithFormat:@"LineStart=%d, LineEnd=%d, ContentsEnd=%d", lineStart, lineEnd, contEnd];
文字位置は0から数え、改行コードは1文字として扱います。
サンプルコードの例文の文字位置は下表のようになります。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
H | a | r | u | k | a | \r | C | h | i | h | a | y | a | \n |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
Y | u | k | i | h | o | \r | \n | Y | a | y | o | i |
改行コードを跨がない範囲を指定(19文字目から2文字分、つまりYukihoの『ho』)すると、
LineStart=15, LineEnd=23, ContentsEnd=21
という結果になります。
開始位置はともかく、終端位置が一つずれているように感じますが、『その一つ手前までが範囲』という解釈のようです。
(その方が『終端位置から文字列を追加』などをしようとした際に指定し易いからと思われます)
実際、改行コードを2つ跨いだ範囲指定(10文字目から14文字分、ChihayaのhayaからYayoiのYまで)をすると、
LineStart=7, LineEnd=28, ContentsEnd=28
という結果になりますが、実際には28文字目は存在しません(28文字目を表示しようとするとエラーになります)。
・- lineRangeForRange:
– (NSRange)lineRangeForRange:(NSRange)aRange
指定した範囲にある行の全体範囲を返します(改行コードも含みます)
aRange:検索する行のある範囲を指定します
NSString *オブジェクト1 = @"Haruka\rChihaya\nYukiho\r\nYayoi";
NSRange rng = [オブジェクト1 lineRangeForRange:NSMakeRange(12, 5)] ;
NSLog([NSString stringWithFormat:@"Location=%d, Length=%d" , rng.location, rng.length]); // {7, 16}
NSRange rng = [オブジェクト1 lineRangeForRange:NSMakeRange(12, 5)] ;
NSLog([NSString stringWithFormat:@"Location=%d, Length=%d" , rng.location, rng.length]); // {7, 16}
・– getParagraphStart:end:contentsEnd:forRange:
– (void)getParagraphStart:(NSUInteger *)startIndex end:(NSUInteger *)endIndex contentsEnd:(NSUInteger *)contentsEndIndex forRange:(NSRange)aRange
指定した範囲の段落の開始位置と終了位置(改行コード含む/含まない)を返します
段落を判別する記号(改行コード)は次の通りです
・U+000D:\rまたは CR(Carriage return:行頭への復帰。行送りは含まない)
・U+000A:\nまたは LF(Line Feed:行送り。行頭への復帰は含まない)
・U+2029:Unicode paragraph separator(段落区切り記号)
・ またはCRLF
– getLineStart:end:contentsEnd:forRange:との違いはUnicode line separator(行区切り記号:U+2028)を判別するかどうかです
getLine~ではUnicode line separatorにも反応し行末と判断しますが、getParagraph~は段落末と判断せずスルーします(getLine~はUnicode paragraph separatorも行末と判断します)
逆に言えば、本来段落末(Unicode paragraph separator)だけに反応してほしいgetLine~が、改行コードである 、\rや\nにも反応してしまいますので注意が必要です
startIndex:指定した範囲にある段落の先頭文字位置を返します
必要が無い場合はNULLを指定します
lineEndIndex:指定した範囲にある段落の改行コード含む終端文字位置を返します
必要が無い場合はNULLを指定します
contentsEndIndex:指定した範囲にある段落の改行コード含まない終端文字位置を返します
必要が無い場合はNULLを指定します
aRange:検索する段落のある範囲を指定します
この範囲は検索するNSStringオブジェクトより大きく設定してはいけません
超える範囲を設定した場合、NSRangeExceptionで例外を発生させます
unsigned int paraStart, paraEnd, contEnd;
NSString *オブジェクト1 = @"Haruka\u2028 Chihaya \u2029Yukiho\r\nYayoi";
[オブジェクト1 getParagraphStart:¶Start end:¶End contentsEnd:&contEnd forRange:NSMakeRange(2, 2)] ;
NSString *オブジェクト2 = [NSString stringWithFormat:@"ParagraphStart=%d, ParagraphEnd=%d, ContentsEnd=%d", paraStart, paraEnd, contEnd];
NSString *オブジェクト1 = @"Haruka\u2028 Chihaya \u2029Yukiho\r\nYayoi";
[オブジェクト1 getParagraphStart:¶Start end:¶End contentsEnd:&contEnd forRange:NSMakeRange(2, 2)] ;
NSString *オブジェクト2 = [NSString stringWithFormat:@"ParagraphStart=%d, ParagraphEnd=%d, ContentsEnd=%d", paraStart, paraEnd, contEnd];
– getLineStart:end:contentsEnd:forRange:のサンプル文字列の改行コードを、Unicode line separatorとUnicode paragraph separatorに置き換えました
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
H | a | r | u | k | a | \u2028 | C | h | i | h | a | y | a | \u2029 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
Y | u | k | i | h | o | \r | \n | Y | a | y | o | i |
改行コードを跨がない範囲を指定(2文字目から2文字分、つまりHarukaの『ru』)すると、
LineStart=0, LineEnd=15, ContentsEnd=14
という結果になります。
前述の通り、6文字目のUnicode line separatorを\rや\nにすると、
LineStart=0, LineEnd=7, ContentsEnd=6
になります
・– paragraphRangeForRange:
– (NSRange)paragraphRangeForRange:(NSRange)aRange
指定した範囲にある段落の全体範囲を返します(Unicode paragraph separatorも含みます)
aRange:検索する行のある範囲を指定します
NSString *オブジェクト1 = @"Haruka\u2028
Chihaya\u2029
Yukiho\r\nYayoi";
NSRange rng = [オブジェクト1 paragraphRangeForRange:NSMakeRange(2, 2)] ;
NSLog([NSString stringWithFormat:@"Location=%d, Length=%d" , rng.location, rng.length]); // {0, 15}
NSRange rng = [オブジェクト1 paragraphRangeForRange:NSMakeRange(2, 2)] ;
NSLog([NSString stringWithFormat:@"Location=%d, Length=%d" , rng.location, rng.length]); // {0, 15}
●合成済文字範囲の判別(Determining Composed Character Sequences)
・– rangeOfComposedCharacterSequenceAtIndex:
– (NSRange)rangeOfComposedCharacterSequenceAtIndex:(NSUInteger)anIndex
指定した位置の文字範囲を判別して返します
Unicodeでは結合文字という、濁点や半濁点などを前の文字と合成して1文字として表示するものがあります。
(他にも色々ありますのでWikipedia/Unicodeの互換文字を参照してください)
このように結合文字の場合、表示上1文字でもコード上では複数文字で構成されている場合が有るため、その検出に使えます
anIndex:文字範囲を判別する文字位置を指定します
NSString *オブジェクト = @"ゆきぽ\u2028
ゆきほ\u309A
\u2029
";
NSRange rng = [オブジェクト rangeOfComposedCharacterSequenceAtIndex:6];
NSLog([NSString stringWithFormat:@"Location=%d, Length=%d, %@" , rng.location, rng.length, [str substringWithRange:NSMakeRange(4, 4)]]);
NSRange rng = [オブジェクト rangeOfComposedCharacterSequenceAtIndex:6];
NSLog([NSString stringWithFormat:@"Location=%d, Length=%d, %@" , rng.location, rng.length, [str substringWithRange:NSMakeRange(4, 4)]]);
サンプルコードの文字列は下記のようになっています
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
ゆ | き | ぽ | \u2028 | ゆ | き | ほ | \u309A | \u2029 |
7文字目は『゚(U+309A)』で、6文字目と合わせて表示上は『ぽ』となりますが、結果は
Location=6, Length=2, ゆきぽ
となります
ちなみに、rangeOfComposedCharacterSequenceAtIndex:2、NSMakeRange(0, 3)では、
Location=2, Length=1, ゆきぽ
という結果になります
・– rangeOfComposedCharacterSequencesForRange:
– (NSRange)rangeOfComposedCharacterSequencesForRange:(NSRange)range
指定した範囲の文字範囲を判別して返します
Unicodeでは結合文字という、濁点や半濁点などを前の文字と合成して1文字として表示するものがあります。
(他にも色々ありますのでWikipedia/Unicodeの互換文字を参照してください)
このように結合文字の場合、表示上1文字でもコード上では複数文字で構成されている場合が有るため、その検出に使えます
range:文字範囲を判別する範囲を指定します
この範囲は検索するNSStringオブジェクトより大きく設定してはいけません
NSString *オブジェクト = @"ゆきぽ\u2028
ゆきほ\u309A
\u2029";
NSRange rng = [オブジェクト rangeOfComposedCharacterSequenceForRange:NSMakeRange(6, 3)];
NSLog([NSString stringWithFormat:@"Location=%d, Length=%d, %@" , rng.location, rng.length, [str substringWithRange:NSMakeRange(4, 4)]]);
NSRange rng = [オブジェクト rangeOfComposedCharacterSequenceForRange:NSMakeRange(6, 3)];
NSLog([NSString stringWithFormat:@"Location=%d, Length=%d, %@" , rng.location, rng.length, [str substringWithRange:NSMakeRange(4, 4)]]);
範囲指定で4文字目から3文字分(ゆきほ)を指定すると、
Location=4, Length=4, ゆきぽ
となり、結合文字を認識して数えている事が分かります
参考文献
・NSString Class Reference
・Cocoa APIとか(iPhoneとか)/NSString
・Studying Cocoa/NSString
・Wikipedia/改行コード
・Predicate Programming Guide
・String Programming Guide for Cocoa
・合成文字対応
・Wikipedia/Unicodeの互換文字