NSString *temp = @"/tmp/scratch";
Objective-Cおいて、文字の内部表現はUTF-16であり、unichar型はその1文字を扱う型である。
しかし、プログラムソースはUTF-8で保存される。
従って、ソース上は、@""の中はUTF-8文字列ということになる。
char *utf8String = "これが存在すると仮定しなさい"; // ソースファイルはUTF-8なので直接記述出来る NSString *stringFromUTFString = [[NSString alloc] initWithUTF8String:utf8String]; // NSData *shiftJISData = "これが存在すると仮定しなさい"; // 実際にはSHIFT-JISで記述した文字列はUTF-8ファイル上では正しく表示されない NSString *stringFromShiftJISData = [[NSString alloc] initWithData:shiftJISData encoding:NSShiftJISStringEncoding];initWithData:encoding:メソッドは、NSDataオブジェクトに保存された文字列データをNSStringオブジェクトに変換出来る。
// NSString → NSData unichar ellipsis = 0x2026; NSString *theString = [NSString stringWithFormat:@"To be continued%C", ellipsis]; NSData *asciiData = [theString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]; // NSData → NSString NSString *asciiString = [[NSString alloc] initWithData:asciiData encoding:NSASCIIStringEncoding]; // NSLog(@"Original: %@ (長さ %d)", theString , [theString length] ); NSLog(@"Converted: %@ (長さ %d)", asciiString, [asciiString length]); // 表示t: // Original: To be continued… (長さ 16) // Converted: To be continued... (長さ 18)
|
重要 NSStringは C文字列からNSStringを生成する多くのメソッドを提供する (stringWithCString:、initWithCString:、initWithCString:length:、initWithCStringNoCopy:length:freeWhenDone:等)。 これらのメソッドはデフォルトC文字列エンコードを使い、そのエンコードの変換において文字情報を失うかもしれない。 これらのメソッドは極力使わないこと。 |
const char *cString = [@"Hello, world" UTF8String];あなたが受信するC文字列は、一時的なオブジェクトによって所有されて、自動割り当て解除が起こるとき無効になる。 あなたがC文字列を永久保存したいならば、バッファを作り、const char* の内容をコピーしなければならない。
|
重要 NSStringはC文字列を直接使うメソッドを数多く提供する。 (たとえば、cString、cStringLength、lossyCString、getCString:、getCString:maxLength:、 getCString:maxLength:range:remainingRange:等)。 これらのメソッドはデフォルトC文字列エンコードを使い、そのエンコードの変換において文字情報を失うかもしれない。 これらのメソッドは極力使わないこと。 |
// Objective-Cソース NSString *greeting = NSLocalizedString(@"Hello", @"こんにちわ"); // @"Hello"がキー名、@"こんにちは"は単なる注釈
/* localizable.stringsファイル */ /* これがローカライズの実際を持つファイル */ "Hello" = "こんにちわ";アプリケーションのローカライズについては「国際化プログラミングガイド」を見なさい。
NSString *beginning = @"beginning"; NSString *alphaAndOmega = [beginning stringByAppendingString:@" and end"]; // alphaAndOmega は @"beginning and end" となるinitWithFormat:、stringWithFormat:、とstringByAppendingFormat: メソッドは、 テンプレートによっていくつかの文字列を結合することもできる; 詳細については 「フォーマット文字列オブジェクト で述べる。
NSString *source = @"0123456789";
NSString *firstFour = [source substringToIndex:4];
// firstFour は @"0123"
NSString *allButFirstThree = [source substringFromIndex:3];
// allButFirstThree は @"3456789"
NSRange twoToSixRange = NSMakeRange(2, 4);
NSString *twoToSix = [source substringWithRange:twoToSixRange];
// twoToSix は @"2345"
NSArray *split = [source componentsSeparatedByString:@"45"];
// split は { @"0123", @"6789" } を含む
あなたが文字位置番号よりもパターンマッチを使って文字列を抽出する必要があるならば、
スキャンを使わなければならない。それについては
「スキャン」を見なさい。
NSString *string1 = [NSString stringWithFormat:@"A 文字列: %@, a float: %1.2f",@"文字列", 31415.9265];
// string1 は "A 文字列: 文字列, a float: 31415.93"
NSNumber *number = [NSNumber numberWithInt:1234];
NSDictionary *dictionary = [NSDictionary dictionaryWithObject:[NSDate date] forKey:@"date"];
NSString *baseString = @"Base 文字列.";
NSString *string2 = [baseString stringByAppendingFormat:@" A number: %@, a dictionary: %@", number, dictionary];
// string2 は "Base 文字列. A number: 1234, a dictionary: {date = 2005-10-17 09:02:01 -0700; }"
NSString *s = [NSString stringWithFormat:@"Long %C dash", 0x2014];UTF-16で0x2014はUTF-8では 0xe28094 なので、それを文字列の中に直接記述した場合は、以下のように書ける。
NSString *s = [NSString stringWithUTF8String:"Long \xe2\x80\x94 dash"];なお、ソースコードの中に直接0x80~0xffの文字を含むことは安全でない。
NSString *string = @"A contrived string %@";
NSLog(string);
// アプリケーションは実行時エラーを発生する
このような場合、以下のように%@を経由して表示させると良い。
NSString *string = @"A contrived string %@"; NSLog(@"%@", string); // 出力: contrived string %@
| 記述子 | 内容 |
| %@ | Objective-C オブジェクトで、
有効ならばdescriptionWithLocale:、だめならdescriptionで返ってくる文字列。 CFCopyDescription関数の結果を返すことで、CFTypeRef オブジェクトでも動作する。 |
| %% | % 文字そのもの |
| %d, %D, %i | 符号付き 32ビット整数 (int) |
| %u, %U | 符号なし 32ビット整数 (符号なし int) |
| %hi | 符号付き 16ビット整数 (short) |
| %hu | 符号なし 16ビット整数 (符号なし short) |
| %qi | 符号付き 64ビット整数 (long long) |
| %qu | 符号なし 64ビット整数 (符号なし long long) |
| %x | 符号なし 32ビット整数 (符号なし int), 0~9数字と英小文字 a~fで出力される16進数 |
| %X | 符号なし 32ビット整数 (符号なし int), 0~9数字と英大文字 A~Fで出力される16進数 |
| %qx | 符号なし 64ビット整数 (符号なし long long), 0~9数字と英小文字 a~fで出力される16進数 |
| %qX | 符号なし 64ビット整数 (符号なし long long), 0~9数字と英大文字 A~Fで出力される16進数 |
| %o, %O | 符号なし 32ビット整数 (符号なし int), 8進数出力 |
| %f | 64ビット浮動小数点値 (double) |
| %e | 64ビット浮動小数点値 (double)、 累乗の指数を導入するために、英小文字eを用いた指数表記で出力される |
| %E | 64ビット浮動小数点値 (double)、 累乗の指数を導入するために、英大文字Eを用いた指数表記で出力される |
| %g | 64ビット浮動小数点値 (double)、 累乗の指数が~4より少ないか、精度以上であるならば、%eで出力される。さもなければ%fで出力する。 |
| %G | 64ビット浮動小数点値 (double), 累乗の指数が~4より少ないか、精度以上であるならば、%eで出力される。さもなければ%fで出力する。 |
| %c | 8ビット符号なし文字 (符号なし char)、 ASCII 文字か、非ASCII 文字なら8進数表記の\\dddまたはUnicode 16進数フォーマットの \\udddd(dは数字)で NSLog() によって出力される |
| %C | 16ビット Unicode 文字 (unichar)、 ASCII 文字か、非ASCII 文字なら8進数表記の\\dddまたはUnicode 16進数フォーマットの \\udddd(dは数字)で NSLog() によって出力される |
| %s | Nullで終端する 8ビット符号なし文字の配列。 %sは、たとえば、UTF-8よりもむしろシステム・エンコードで、その入力を解釈する。 |
| %S | Nullで終端する 16ビット Unicode 文字の配列 |
| %p | Void ポインター (void *)、0xで始まる0~9数字と英小文字 a~fで出力される16進数 クラスインスタンスの場合は、そのアドレス |
| %L | これに続くa、A、e、E、f、F、g、またはG変換記述子が、slong double 引数に適用されることを示している、長さ修飾子 |
| %a | 64ビット浮動小数点値 (double)、 0xで始まり、小数点の前に1桁の16進数、指数表記のために英小文字pを使って累乗指数で出力する |
| %A | 64ビット浮動小数点値 (double)、 0Xで始まり、小数点の前に1桁の16進数、指数表記のために英大文字Pを使って累乗指数で出力する |
| %F | 64ビット浮動小数点値 (double)、10進数で出力される |
| %z | これに続くd、i、o、u、xまたはX変換記述子が、size_tまたは対応する符号付き整数型引数に適用されることを示している、長さ修飾子 |
| %t | これに続くd、i、o、u、xまたはX変換記述子が、size_tまたは対応する符号なし整数型引数に適用されることを示している、長さ修飾子 |
| %j | これに続くd、i、o、u、xまたはX変換記述子が、intmax_tまたはuintmax_tに適用されることを示している、長さ修飾子 |
NSString *path = ...;
NSError *error;
NSString *stringFromFileAtPath = [[NSString alloc]initWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:&error];
if (stringFromFileAtPath == nil) {
// エラーが発生したとき
NSLog(@"Error reading file at %@\n%@", path, [error localizedFailureReason]);
~
}
以下の例のように、データオブジェクトを使って文字列を初期化することも出来る。
ここでも正しいエンコードを指定しなければならない。
NSString *path = ...; NSData *data = [NSData dataWithContentsOfFile:path]; // データがUTF8である NSString *string = [NSString stringWithUTF8String:[data bytes]]; // データが他のエンコードである。たとえば ISO-8859-1 NSString *string = [[NSString alloc] initWithData:data encoding: NSISOLatin1StringEncoding];
|
重要 NSStringは エンコードを指定することなくファイルやURLを読み込むためのメソッド (stringWithContentsOfFile: と initWithContentsOfURL:のようなもの)を提供するが、 これらのメソッドではあなたは情報を失うか、使っているデータを壊すかもしれない。 極力使わないように。 |
NSString *path = ...;
NSString *string = ...;
NSError *error;
BOOL ok = [string writeToFile:path atomically:YES encoding:NSUnicodeStringEncoding error:&error];
if (!ok) {
// エラーが発生したとき
NSLog(@"Error writing file at %@\n%@", path, [error localizedFailureReason]);
~
}
| 検索メソッド | 比較メソッド |
| rangeOfString: | compare: |
| rangeOfString: options: | compare: options: |
| rangeOfString: options: range: | compare: options: range: |
| rangeOfString: options: range: locale: | compare: options: range: locale: |
| rangeOfCharacterFromSet: | |
| rangeOfCharacterFromSet: options: | |
| rangeOfCharacterFromSet: options:range: |
|
重要 ユーザーに表示するソート結果については、あなたはローカライズされた比較を常に使わなければならない。 従って、一般的にcompare: または caseInsensitiveCompare:の代わりに localizedCompare: またはlocalizedCaseInsensitiveCompare:を使うべきである。 あなたが、Finderで表示されるのと同じ順番で文字列を比較したいなら、 compare:options:range:locale:にユーザーのロケールと以下のオプション NSCaseInsensitiveSearch、 NSNumericSearch、 NSWidthInsensitiveSearch や NSForcedOrderingSearchで使うことが出来る。 例については 「Finderのような文字列ソート」を見なさい。 |
| 検索 | オプションの効果 |
| NSCaseInsensitiveSearch | 文字の大文字小文字の区別を無視する |
| NSLiteralSearch | バイト単位で比較する。 他では同じと判定される合成文字も不一致と判定される。 このオプションを使うといくつかの操作が劇的に速くなる。 |
| NSBackwardsSearch | 範囲の後ろから前に検索する |
| NSAnchoredSearch | 範囲の先頭または終端の文字だけを検索する。 先頭または終端で一致しないということは、全体としては不一致を意味する。 |
| NSNumericSearch | compare:options: メソッドを一緒に使うとき、
数字の一群は、比較時に数値として処理される。 たとえば、Filename9.txt < Filename20.txt < Filename100.txtである。 |
NSString *searchString = @"age";
NSString *beginsTest = @"Agencies";
// ~~~
NSRange prefixRange = [beginsTest rangeOfString:searchString options:(NSAnchoredSearch | NSCaseInsensitiveSearch)];
// prefixRange = {0, 3}
NSString *endsTest = @"BRICOLAGE";
// ~~~
NSRange suffixRange = [endsTest rangeOfString:searchString options:(NSAnchoredSearch | NSCaseInsensitiveSearch | NSBackwardsSearch)];
// suffixRange = {6, 3}
NSString *string1 = @"string1"; NSString *string2 = @"string2"; NSComparisonResult result; result = [string1 compare:string2]; // result = -1 (NSOrderedAscending)NSNumericSearch オプションを使うことで文字列を数値として比較することが出来る:
NSString *string10 = @"string10"; NSString *string2 = @"string2"; NSComparisonResult result; result = [string10 compare:string2]; // result = -1 (NSOrderedAscending) result = [string10 compare:string2 options:NSNumericSearch]; // result = 1 (NSOrderedDescending)caseInsensitiveCompare: と localizedCaseInsensitiveCompare:で大文字小文字を無視した比較ができる。
NSString *string_a = @"Aardvark"; NSString *string_A = @"AARDVARK"; result = [string_a compare:string_A]; // result = 1 (NSOrderedDescending) result = [string_a caseInsensitiveCompare:string_A]; // result = 0 (NSOrderedSame) // [string_a compare:string_A options:NSCaseInsensitiveSearch]と等価
int finderSortWithLocale(id string1, id string2, void *locale)
// ロケール対応のソート関数
{
static NSStringCompareOptions comparisonOptions =
NSCaseInsensitiveSearch | NSNumericSearch |
NSWidthInsensitiveSearch | NSForcedOrderingSearch;
NSRange string1Range = NSMakeRange(0, [string1 length]);
return [string1 compare:string2 options:comparisonOptions range:string1Range locale:(NSLocale *)locale];
}
あなたは、sortedArrayUsingFunction:context:へのパラメータとして、
コンテキストとしてユーザの現在のロケールと共に、関数を渡す。
NSArray *stringsArray = [NSArray arrayWithObjects:
@"String 1",
@"String 21",
@"String 12",
@"String 11",
@"String 02", nil];
NSArray *sortedArray = [stringsArray sortedArrayUsingFunction:finderSortWithLocale context:[NSLocale currentLocale]];
// sortedArray は { "String 1", "String 02", "String 11", "String 12","String 21" }を含む
NSArray *arr = [myString componentsSeparatedByString:@"\n"];
である。しかしながらこれは、\r, \r\n, または Unicode セパレーターという文字列にあるような、
改段落または改行に多くの方法があるという事実を無視する。
その代わりに、あなたはlineRangeForRange: または getParagraphStart:end:contentsEnd:forRange: のような
メソッドを利用することが出来る。
NSString *string = @"分割する文字列~";
unsigned length = [string length];
unsigned paraStart = 0, paraEnd = 0, contentsEnd = 0;
NSMutableArray *array = [NSMutableArray array];
NSRange currentRange;
while (paraEnd < length) {
[string getParagraphStart:¶Start end:¶End contentsEnd:&contentsEnd forRange:NSMakeRange(paraEnd, 0)];
currentRange = NSMakeRange(paraStart, contentsEnd - paraStart);
[array addObject:[string substringWithRange:currentRange]];
}
NSString *myString = @"some text in an NSString..."; NSCharacterSet *characterSet = [NSCharacterSet uppercaseLetterCharacterSet]; NSRange letterRange; letterRange = [myString rangeOfCharacterFromSet:characterSet];このプログラムを実行するとrangeOfCharacterFromSet:が呼び出され、 letterRange.location は "NSString"の最初の“N”の文字位置となる。 文字列の最初の文字が'S'なら、それは0=先頭になる。
NSCharacterSet *finalCharSet; // 文字群(固定) NSMutableCharacterSet *workingSet; // 中間ワーク(可変) // workingSet = [[NSCharacterSet alphanumericCharacterSet] mutableCopy]; // 英数字 [workingSet addCharactersInString:@";:,."]; // 句読点を追加 finalCharSet = [workingSet copy]; // 最終文字群を作成 [workingSet release]; // 中間ワークを解放Unicodeコードポイントを使っているカスタム文字セットを定義するためには、 以下に類似したプログラムを使いなさい。 これは、改行と行セパレーター文字を含む文字セットを作っている。
UniChar chars[] = {0x000C, 0x2028};
NSString *string = [[NSString alloc] initWithCharacters:chars length:sizeof(chars) / sizeof(UniChar)];
NSCharacterSet *chset = [NSCharacterSet characterSetWithCharactersInString:string];
[string release];
NSString *filename=@"filename"; NSString *absolutePath; NSData *charSetRep; BOOL result; absolutePath = [filename stringByStandardizingPath]; charSetRep = [finalCharSet bitmapRepresentation]; result = [charSetRep writeToFile:absolutePath atomically:YES];習慣的に、文字セットファイル名は拡張子.bitmapを使う。 あなたが他の人のために文字セット・ファイルを公開するつもりなら、この規則に従わなければならない。 .bitmap拡張子で文字セット・ファイルを読むためには、characterSetWithContentsOfFile: メソッドを使う。
float aFloat;
NSScanner *theScanner = [NSScanner scannerWithString:aString];
while ([theScanner isAtEnd] == NO) {
[theScanner scanFloat:&aFloat];
~
}
スキャナーにおいて英大文字小文字を区別するかどうかの設定はsetCaseSensitive: メソッドで行う。
デフォルトのスキャナーは大文字小文字を区別しない。
137 small cases of bananasあなたはコンテナの型とコンテナの数をscanUpToString:intoString:を使って調べることが出来る。 それは以下の例のようになる。
NSString *bananas = @"137 small cases of bananas";
NSString *separatorString = @" of";
NSScanner *aScanner = [NSScanner scannerWithString:bananas];
NSInteger anInteger;
[aScanner scanInteger:&anInteger];
NSString *container;
[aScanner scanUpToString:separatorString intoString:&container];
検索文字列=separatorStringが" of"であることに注意しなければならない。
デフォルトで、スキャナは空白文字を無視するので、整数の後のスペース文字は無視される。
しかし、一旦スキャナが文字を集め始めるならば、検索文字列に達するまで、すべての文字は出力文字列に追加される。
このように、検索文字列が"of"(前にスペースがない)なら、最初のコンテナ値は"small cases "(後のスペースを含む)である。
検索文字列が" of"(前にスペースを含む)なら、最初のコンテナ値は"small cases"(後ろにスペースがない)である。
[aScanner scanString:separatorString intoString:NULL]; NSString *product = [[aScanner string] substringFromIndex:[aScanner scanLocation]]; // これも使える: // product = [bananas substringFromIndex:[aScanner scanLocation]];
Product: Acme Potato Peeler; Cost: 0.98 73
Product: Chef Pierre Pasta Fork; Cost: 0.75 19
Product: Chef Pierre Colander; Cost: 1.27 2
以下の例は、製品名(product names)と経費(costs)を引き抜くために、交互のスキャン活動を使う(経費は、単純化のため、floatと解釈する)。
そして、予想される部分文字列「Product:」と「Cost:」をセミコロン(;)と同様に飛ばす。
スキャナがデフォルトのによって空白文字と改行文字をスキップするので、
ループがそれらのために特別な処理をしていない点にも注目しなさい
(特に、最終的な整数を取り出すためにさらなる空白文字処理をする必要がない)。
#define PRODUCT @"Product:"
#define COST @"Cost:"
NSString *string = PRODUCT @" Acme Potato Peeler; " COST " 0.98 73\n"
PRODUCT @" Chef Pierre Pasta Fork; " COST " 0.75 19\n"
PRODUCT @" Chef Pierre Colander; " COST " 1.27 2\n"; // ここでは@文字列の結合を使っている
NSCharacterSet *semicolonSet;
NSScanner *theScanner;
NSString *productName;
float productCost;
NSInteger productSold;
semicolonSet = [NSCharacterSet characterSetWithCharactersInString:@";"];
theScanner = [NSScanner scannerWithString:string];
while ([theScanner isAtEnd] == NO) {
if ([theScanner scanString:PRODUCT intoString:NULL] &&
[theScanner scanUpToCharactersFromSet:semicolonSet
intoString:&productName] &&
[theScanner scanString:@";" intoString:NULL] &&
[theScanner scanString:COST intoString:NULL] &&
[theScanner scanFloat:&productCost] &&
[theScanner scanInteger:&productSold]
) {
NSLog(@"Sales of %@: $%1.2f", productName, productCost * productSold);
}
}
NSString *path; NSString *standardizedPath; path = @"/usr/bin/./grep"; standardizedPath = [path stringByStandardizingPath]; // standardizedPath: /usr/bin/grep path = @"~me"; standardizedPath = [path stringByStandardizingPath]; // standardizedPath /Users/me path = @"/usr/include/objc/.."; standardizedPath = [path stringByStandardizingPath]; // standardizedPath: /usr/include path = @"/private/usr/include"; standardizedPath = [path stringByStandardizingPath]; // standardizedPath: /usr/include
NSString *meHome = [@"~me" stringByExpandingTildeInPath]; // meHome = @"/Users/me" NSString *mePublic = [@"~me/Public" stringByExpandingTildeInPath]; // mePublic = @"/Users/me/Public"あなたはカレントユーザーと与えられたユーザーのホームディレクトリをそれぞれ、NSHomeDirectoryとNSHomeDirectoryForUser で見つけることが出来る (iOSでは関係ないはず) 。
NSString *currentUserHomeDirectory = NSHomeDirectory(); NSString *meHomeDirectory = NSHomeDirectoryForUser(@"me");一般的に、現在のユーザーのために標準的なディレクトリを見つけるためには、 関数NSSearchPathForDirectoriesInDomainsを使わなければならない。 たとえば、
NSString *documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
の代わりにこれを使うべきである。
NSString *documentsDirectory;
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if ([path count] > 0) {
documentsDirectory = [path objectAtIndex:0];
}
| pathExtension | レシーバーの拡張子を返す |
| stringByDeletingPathExtension | レシーバーから拡張子を(存在すれば)削除することによって作られる新しい文字列を返す |
| stringByDeletingLastPathComponent | レシーバーから最後のパス構成要素を削除することによって作られる新しい文字列を返す ファイル名+拡張子を除いたパス名部分のみを返す |
NSString *documentPath = @"~me/Public/Demo/readme.txt"; NSString *documentDirectory = [documentPath stringByDeletingLastPathComponent]; // documentDirectory = @"~me/Public/Demo" NSString *documentFilename = [documentPath lastPathComponent]; // documentFilename = @"readme.txt" NSString *documentExtension = [documentPath pathExtension]; // documentExtension = @"txt"
ReadMe.txt
readme.html
readme.rtf
recondite.txt
test.txt
あなたは、"~/Demo/r"としての可能性あるファイルを見つけることができる。
NSString *partialPath = @"~/Demo/r";
NSString *longestCompletion;
NSArray *outputArray;
unsigned allMatches = [partialPath completePathIntoString:&longestCompletion
caseSensitive:NO
matchesIntoArray:&outputArray
filterTypes:NULL];
// allMatches = 3
// longestCompletion = @"~/Demo/re"
// outputArray = (@"~/Demo/readme.html", "~/Demo/readme.rtf","~/Demo/recondite.txt")
また、"~/Demo/r" で、拡張子“.txt”または“.rtf”を持つファイルを見つけることができる。
NSArray *filterTypes = [NSArray arrayWithObjects:@"txt", @"rtf", nil];
unsigned textMatches = [partialPath completePathIntoString:&outputName
caseSensitive:NO
matchesIntoArray:&outputArray
filterTypes:filterTypes];
// allMatches = 2
// longestCompletion = @"~/Demo/re"
// outputArray = (@"~/Demo/readme.rtf", @"~/Demo/recondite.txt")