文字列操作プログラミングガイド ( i 0.0.0)

52 

Loading.... (view fulltext now)

Loading....

Loading....

Loading....

Loading....

全文

(1)

文字列操作

(2)

目次

文字列操作プログラミングガイド(Cocoa):はじめに

5 対象読者 5 この書類の構成 5 関連項目 6

文字列

7

文字列オブジェクトの生成と変換

8 文字列を生成する 8 C文字列やデータからNSStringを生成する 8 文字列の整形 10 ユーザ向けに表示する文字列 10 文字列を結合、抽出する 11 C文字列を取得する 12 変換メソッドの概要 12

文字列オブジェクトの整形

13 書式整形の基本事項 13 文字列と非ASCII文字 14 NSLogとNSLogv 14

文字列の書式指定子

16 書式指定子 16 プラットフォームによる違い 18

ファイルやURLに対する文字列の読み書き

20 ファイルやURLから読み込む 20 エンコーディングが分かっているデータを読み込む 20 エンコーディングが未知のデータを読み込む 21 ファイルやURLに書き出す 22 まとめ 22

文字列の検索、比較、整列

23 検索/比較メソッド 23

(3)

文字列を検索する 23 文字列を比較、整列する 24 検索/比較のオプション 25 例 25 大文字と小文字を区別しない、前方一致/後方一致の検索 25 文字列の比較 26 Finderに現れるのと同じ順序に整列する 27

単語、段落、改行

29 単語の区切り 29 行や段落の区切り文字 29 文字列を「段落ごとに」区切る 30

文字と書記素クラスタ

31

文字集合

34 文字集合に関する基本事項 34 文字集合を生成する 34 処理性能に関する考慮事項 35 文字集合ファイルを生成する 36 標準文字集合とUnicode規格における定義 36

字句解析器

37 字句解析器を生成する 37 字句解析器の使い方 37 例 39 ローカライズ 40

ファイルパスの文字列表現

41 パスの表現 41 ユーザディレクトリ 42 パスの構成要素 43 ファイル名の補完 44

文字列の描画

45

書類の改訂履歴

46

索引

49

(4)

文字列の書式指定子

16

表 1 NSStringやCFStringの整形メソッド/関数で使える書式指定子 16

表 2 NSStringやCFStringの整形メソッド/関数に指定できる長さ修飾子 17

(5)

本資料『文字列操作プログラミングガイド(Cocoa) 』では、文字列を生成、検索、連結、描画する 方法を解説します。さらに、文字列からある一群の文字を検索するために用いる「文字集合」や、数 値と文字列を相互に変換する「字句解析器」についても説明します。

対象読者

文字列や文字集合を直接操作するプログラムの開発者を読者として想定しています。

この書類の構成

この資料は次の各章から成ります。 ● “文字列” (7 ページ)ではCocoaの文字列オブジェクトの特徴を説明します。“文字列オブジェクトの生成と変換” (8 ページ)では、NSStringやそのサブクラスである NSMutableStringのインスタンス(文字列オブジェクト)を生成する方法、各種の文字エンコー ド方式を変換する方法を解説します。 ● “文字列オブジェクトの整形” (13 ページ)ではNSStringオブジェクトの整形方法を説明します。 ● “文字列の書式指定子” (16 ページ)では、printf方式の書式指定子のうち、NSStringオブジェ クトに対して適用できるものを紹介します。 ● “ファイルやURLに対する文字列の読み書き” (20 ページ)では、ファイルやURLを指定して文字 列を読み書きする方法を述べます。 ● “文字列の検索、比較、整列” (23 ページ)では、文字列中の文字や部分文字列を検索する方法、 他の文字列と比較する方法を説明します。 ● “単語、段落、改行” (29 ページ)では、単語、段落、改行を判定する方法を説明します。“文字と書記素クラスタ” (31 ページ)では、文字列をユーザが認識できる文字群に分割する方 法を解説します。 ● “文字集合” (34 ページ)では、文字集合オブジェクトの使い方、NSCharacterSetのメソッドを 使って標準あるいは独自の文字集合を生成する手順を説明します。

文字列操作プログラミングガイド(Cocoa):

はじめに

(6)

“字句解析器” (37 ページ)ではNSScannerオブジェクトについて解説します。NSStringオブジェ クト中の文字を分析し、数値や文字列値に変換する機構です。 ● “ファイルパスの文字列表現” (41 ページ)では、文字列をファイルシステム上のパスとして操 作する、NSStringのメソッドを紹介します。 ● “文字列の描画” (45 ページ)では、NSViewに直接文字列を描画する、NSStringクラスのメソッ ドを紹介します。

関連項目

さらに詳しくは、以下の資料を参照してください。 ● 『AttributedStringProgrammingGuide 』は、本資料『文字列操作プログラミングガイド(Cocoa) 』 と密接に関連します。文字列や個々の文字に関する属性(フォント、カーニングなど)を管理す る、NSAttributedStringオブジェクトについて説明しています。

『Data Formatting Guide 』では、テキストを生成、解釈、検証するオブジェクトを使って、デー

タを整形する手順を解説しています。

『Internationalization and Localization Guide 』には、プロジェクトで扱う文字列のローカライズ方

法が載っています。文字列整形用の引数の順序づけに関する説明もあります。

『String Programming Guide for Core Foundation 』では、Core Foundationの不透過型であるCFString

について説明しています。NSStringクラスとは相互に入れ替えて使えます。 文字列操作プログラミングガイド(Cocoa):はじめに

(7)

Cocoaフレームワークにおいて、文字列オブジェクトは文字の並びを表します。文字列をオブジェク トとして表現することにより、オブジェクトが使えるところであればどこでも文字列が使えるように なります。カプセル化による効用もあり、エンコーディングを管理する、効率的に格納する、などの 恩恵を受けながら、単なる文字の配列として扱うことも可能になっています。 文字列オブジェクトは、Unicode文字の配列(すなわちテキスト文字列)として実装されます。不変 (immutable)文字列とは、生成時に内容が決まり、その後は変更できないテキスト文字列のことで す。不変文字列の生成や管理にはNSStringクラスを利用します。一方、生成後いつでも変更できる 可変(mutable)文字列の構築や管理には、NSMutableStringクラスを使います。 NSStringやNSMutableStringで生成他オブジェクトを、文字列オブジェクト(混乱の虞がなければ 単に文字列)と呼びます。C文字列 とは、標準Cの「char *」型を表します。 文字列オブジェクトは、Unicode文字列の配列として表されます。文字列中の文字数はlengthメソッ ドで調べることができます。また、characterAtIndex:メソッドで、文字列中の文字を取得できま す。この2つの「プリミティブ」メソッドが、文字列オブジェクト操作の基本となります。しかし一 般には、文字列全体をひとつの単位として扱うことが多いでしょう。他の文字列と比較する、部分文 字列を抽出する、他の文字列と組み合わせて新しい文字列を生成する、などの操作です。個々の文字 に対する操作が必要な場合、Unicode文字エンコーディング、特に結合文字列について理解しておか なければなりません。詳しくは以下の資料を参照してください。

『The Unicode Standard, Version 4.0 』。The Unicode Consortium、Boston: Addison-Wesley、2003。

ISBN 0-321-18578-1。

Unicode Consortiumのウェブサイト:http://www.unicode.org/

(8)

NSStringおよびそのサブクラスであるNSMutableStringには、文字列オブジェクトを生成する手段 がいくつかあり、いずれもさまざまな文字エンコーディング方式に対応しています。文字列オブジェ クトはその内容を常にUnicode文字で表現するようになっていますが、7ビットASCII、ISO Latin 1、 EUC、Shift-JISなどさまざまなエンコーディングの文字列と相互に変換可能です。クラスメソッド availableStringEncodingsで、変換可能なエンコーディングを取得できます。C文字列と文字列オ ブジェクトを変換する際には、明示的にエンコーディングを指定できます。あるいは、C文字列のデ フォルトのエンコーディング(プラットフォームによって異なり、クラスメソッド defaultCStringEncodingで判定可能)を仮定することも可能です。

文字列を生成する

ソースコード内で文字列オブジェクトを生成する最も簡単な方法は、Objective-Cの「@"..."」という 構文を使うことでしょう。

NSString *temp = @"Contrafibularity";

この方法で文字列定数を生成する場合、UTF-8の文字を使わなければなりません。このオブジェクト はコンパイル時に生成され、プログラムの実行中は常に存在します。コンパイラはモジュール単位で このオブジェクト定数を生成するようになっており、実行中に破棄されることはありません。文字列 定数に対しても、他のオブジェクトと同様、直接メッセージを送信することができます。

BOOL same = [@"comparison" isEqualToString:myString];

C文字列やデータからNSStringを生成する

C文字列からNSStringオブジェクトを生成するには、たとえばinitWithCString:encoding:メソッ ドを使います。この場合、C文字列の文字エンコーディングを正しく指定しなければなりません。ほ かにも、さまざまなエンコーディングの文字から、文字列オブジェクトを生成するメソッドがいくつ かあります。たとえばinitWithData:encoding:メソッドは、NSDataオブジェクトに収容された文 字列データを、NSStringオブジェクトに変換します。 char *utf8String = /* 何らかの値があるものと仮定*/ ;

文字列オブジェクトの生成と変換

(9)

NSString *stringFromUTFString = [[NSString alloc] initWithUTF8String:utf8String];

char *macOSRomanEncodedString = /* 何らかの値があるものと仮定 */ ; NSString *stringFromMORString =

[[NSString alloc] initWithCString:macOSRomanEncodedString encoding:NSMacOSRomanStringEncoding];

NSData *shiftJISData = /* 何らかの値があるものと仮定 */ ; NSString *stringFromShiftJISData =

[[NSString alloc] initWithData:shiftJISData

encoding:NSShiftJISStringEncoding];

以下に、UTF-8文字から成るNSStringオブジェクトをASCIIデータに変換した後、NSStringオブジェ

クトに逆変換する例を示します。

unichar ellipsis = 0x2026;

NSString *theString = [NSString stringWithFormat:@"To be continued%C", ellipsis];

NSData *asciiData = [theString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];

NSString *asciiString = [[NSString alloc] initWithData:asciiData encoding:NSASCIIStringEncoding];

NSLog(@"Original: %@ (length %d)", theString, [theString length]); NSLog(@"Converted: %@ (length %d)", asciiString, [asciiString length]);

// output:

// Original: To be continued… (length 16) // Converted: To be continued... (length 18) 文字列オブジェクトの生成と変換

(10)

注意: NSStringは、任意のバイト列を収容できるデータコンテナではありません。そのよ うな機能が必要な場合はNSDataを使ってください。

文字列の整形

文字列の書式整形には通常、stringWithFormat:メソッドやinitWithFormat:メソッド(ローカラ イズする場合はlocalizedStringWithFormat:)を使います。この系列のメソッド群は、書式文字列 をテンプレートとして用い、別に指定する値(文字列、他のオブジェクト、数値など)を埋め込んで 新たな文字列を生成します。テンプレートに記述できる書式指定子については、“文字列オブジェク トの整形” (13 ページ)を参照してください。 既存の文字列オブジェクトに別の文字列を追記して新しい文字列を生成したいときは、 stringByAppendingString:やstringByAppendingFormat:を使います。後者は書式文字列を指定す るようになっています。

NSString *hString = @"Hello";

NSString *hwString = [hString stringByAppendingString:@", world!"];

ユーザ向けに表示する文字列

ユーザに向けて表示する文字列を生成する場合、アプリケーションのローカライズにも配慮してくだ さい。一般に、アプリケーション上でユーザが目にすることになる文字列を、コード中に直接記述し てはなりません。代わりにローカライズ用辞書のキーを記述してください。この辞書に、言語設定に 応じた文字列を収容します。具体的には、次の例のように、NSLocalizedStringやその系列のマク ロを使います。

NSString *greeting = NSLocalizedStringFromTable

(@"Hello", @"greeting to present in first launch panel", @"greetings");

アプリケーションの国際化について詳しくは、『Internationalization and Localization Guide 』を参照し てください。“Localizing String Resources”には、ローカライズ済み文字列中に可変部分を設け、その順 序を調整する方法の説明が載っています。

文字列オブジェクトの生成と変換 文字列を生成する

(11)

文字列を結合、抽出する

文字列を結合、抽出する手段もさまざま揃っています。2つの文字列を結合する最も簡単な手段は追 記でしょう。stringByAppendingString:メソッドは、レシーバの文字列と引数として指定された文

字列を結合し、新たに文字列を生成して返します。

NSString *beginning = @"beginning";

NSString *alphaAndOmega = [beginning stringByAppendingString:@" and end"]; // alphaAndOmega is @"beginning and end"

テンプレートに従って文字列を結合することも可能で、それにはinitWithFormat:、

stringWithFormat:、stringByAppendingFormat:などのメソッドを使います。詳しくは“文字列オ ブジェクトの整形” (13 ページ)を参照してください。

文字列の先頭または末尾から数えた文字数や範囲を指定して、部分文字列を抽出することも可能で、

substringToIndex:、substringFromIndex:、substringWithRange:などのメソッドを使います。

区切り文字を指定して部分文字列に分割するためにはcomponentsSeparatedByString:メソッドが

あります。使用例を以下に示します。インデックスを指定するメソッドの場合、番号は0から始まる

ことに注意してください。

NSString *source = @"0123456789";

NSString *firstFour = [source substringToIndex:4]; // firstFour is @"0123"

NSString *allButFirstThree = [source substringFromIndex:3]; // allButFirstThree is @"3456789"

NSRange twoToSixRange = NSMakeRange(2, 4);

NSString *twoToSix = [source substringWithRange:twoToSixRange]; // twoToSix is @"2345"

NSArray *split = [source componentsSeparatedByString:@"45"]; // split contains { @"0123", @"6789" }

インデックスではなくパターン照合の方法で文字列を抽出したい場合は、字句解析器を使ってくださ い(“字句解析器” (37 ページ)を参照)。

文字列オブジェクトの生成と変換 文字列を結合、抽出する

(12)

C文字列を取得する

文字列オブジェクトからC文字列を取得するには、UTF8Stringメソッドを使うとよいでしょう。戻り

値型はconst char *で、UTF8エンコーディングの文字列になります。

const char *cString = [@"Hello, world" UTF8String];

戻り値であるC文字列を所有するのは一時オブジェクトで、自動解放の機構が働くと無効になります。 永続的なC文字列が必要な場合は、バッファを生成し、const char *型の戻り値の内容をコピーしな ければなりません。 同様に、Unicodeその他任意のエンコーディングの文字から文字列オブジェクトを生成し、あるいは 任意のエンコーディングでデータを抽出することも可能です。initWithData:encoding:および dataUsingEncoding:は、NSDataオブジェクトとの間でこのような変換を行います。

変換メソッドの概要

文字列オブジェクトの生成や変換によく使われる方法を要約して示します。 書き出し 生成方法 生成/変換元 該当なし @"..."(コンパイラが構築) コード中に記述 UTF8String stringWithUTF8String: UTF8エンコーディングの文字 列 getCharacters: getCharacters:range: stringWithCharacters: length: Unicodeエンコーディングの 文字列 dataUsingEncoding: initWithData: encoding: 任意のエンコーディングの文 字列 該当なし stringByAppendingString: stringByAppendingFormat: 既存の文字列 NSScannerを使用 localizedStringWithFormat: initWithFormat: locale: 書式文字列 該当なし NSLocalizedStringおよびその 系列 ローカライズ済み文字列 文字列オブジェクトの生成と変換 C文字列を取得する

(13)

この章では、書式文字列を使って文字列を生成する方法、書式文字列中に非ASCII文字を入れる方法を

解説し、NSLogやNSLogvを利用する際によくある誤りを示します。

書式整形の基本事項

NSStringは書式文字列が使えます。その構文は、他の書式整形オブジェクトに倣っています。すな

わち、ANSI Cの関数printf()で定義されている書式文字、および任意のオブジェクトを表す「%@」で

す(“文字列の書式指定子” (16 ページ)および「IEEE printf specification」を参照)。書式文字列中に 「%@」があると、該当するオブジェクトがdescriptionWithLocale:メッセージに応答できる場合、

NSStringは当該メッセージを送信してそのテキスト表現を取得します。そうでなければdescription

メッセージを使います。“Localizing String Resources”には、ローカライズ済み文字列中に可変部分を設 け、その順序を調整する方法の説明が載っています。

書式文字列中、「%」という文字は値を埋め込む位置、これに続く文字が整形方法を表します。たと

えば"%d houses"という書式文字列ならば、「%d」の部分を整数値で置き換えることになります。

NSStringは、ANSI Cの関数printf()で定義されている書式文字、および任意のオブジェクトを表す

「@」が使えるようになっています。書式文字列中に「%@」があると、該当するオブジェクトが descriptionWithLocale:メッセージに応答できる場合、NSStringは当該メッセージを送信してそ のテキスト表現を取得します。そうでなければdescriptionメッセージを使います。 値の書式整形方法は、ユーザが現在選択しているロケール、すなわち、数値、日付、その他の書式を 指定するNSDictionaryオブジェクトにも依存します。もっとも、NSStringが使うロケール設定は、 10進数値の区切り文字(NSDecimalSeparatorキーで指定)だけです。ロケールを指定しないメソッ ドの場合、デフォルトのロケールを使うものと想定します。

NSStringのstringWithFormat:メソッドやその系列を使うと、printf型の書式指定子を使った整形

文字列に基づき、値を埋め込んだ文字列を生成できます(“Creating and Converting String

Objects” (8 ページ)を参照)。各種の書式指定子と引数を使って文字列を生成する例を示します。

NSString *string1 = [NSString stringWithFormat:@"A string: %@, a float: %1.2f", @"string", 31415.9265];

// string1 is "A string: string, a float: 31415.93"

文字列オブジェクトの整形

(14)

NSNumber *number = @1234;

NSDictionary *dictionary = @{@"date": [NSDate date]}; NSString *baseString = @"Base string.";

NSString *string2 = [baseString stringByAppendingFormat:

@" A number: %@, a dictionary: %@", number, dictionary];

// string2 is "Base string. A number: 1234, a dictionary: {date = 2005-10-17 09:02:01 -0700; }"

文字列と非ASCII文字

文字列中に非ASCII文字(Unicodeを含む)を入れたいときは、stringWithFormat:、 stringWithUTF8String:などのメソッドを使います。

NSString *s = [NSString stringWithFormat:@"Long %C dash", 0x2014];

Unicodeの文字0x2014(コードポイント)をUTF-8でエンコードすると「\xe2\x80\x94」という3バイ

トになるので、次のようにも記述できます。

NSString *s = [NSString stringWithUTF8String:"Long \xe2\x80\x94 dash"];

NSLogとNSLogv

ユーティリティ関数NSLog()およびNSLogv()は、NSStringの文字列整形機能を援用してエラーメッ

セージをログに出力します。したがって、この関数に引数を指定する際には注意してください。次の ように、文字列中に書式指定子を含めてしまう、という誤りがよくあります。

NSString *string = @"A contrived string %@"; NSLog(string);

// The application will probably crash here due to signal 10 (SIGBUS)

したがって次のように、別の文字列を出力するための整形文字列を指定する方が安全です。

NSString *string = @"A contrived string %@"; NSLog(@"%@", string);

文字列オブジェクトの整形 文字列と非ASCII文字

(15)

// Output: A contrived string %@ 文字列オブジェクトの整形

(16)

この章では文字列整形メソッド/関数に指定できる書式指定子を要約して示します。

書式指定子

NSStringやCFStringの整形メソッド/関数に指定できる書式指定子は、「IEEE printf specification」に

従っています。その要約を表 1 (16 ページ)に示します。なお、位置指定子「n$」を使って、「%1$@ %2$s」のように指定することも可能です。詳しくは「IEEE printf specification」を参照してください。

この書式指定子はNSLog関数でも使えます。 表 1 NSStringやCFStringの整形メソッド/関数で使える書式指定子 説明 指定子 Objective-Cのオブジェクトの文字列表現。descriptionWithLocale:メソッドがあ ればその戻り値、なければdescriptionメソッドの戻り値になります。CFTypeRef オブジェクトに対しても適用可能で、この場合、CFCopyDescription関数の戻り値 を使います。 %@ '%'という文字自身。 %% 符号付き32ビット整数(int)。 %d、%D 符号なし32ビット整数(unsigned int)。 %u、%U

符号なし32ビット整数(unsigned int)の16進表示。数字(0-9)と英小文字(a-f)

で表記。

%x

符号なし32ビット整数(unsigned int)の16進表示。数字(0-9)と英大文字(A-F)

で表記。 %X 符号なし32ビット整数(unsigned int)の8進表示。 %o、%O 64ビット浮動小数点数(double)。 %f 64ビット浮動小数点数(double)の指数表記。指数部の先頭は小文字の「e」。 %e 64ビット浮動小数点数(double)の指数表記。指数部の先頭は大文字の「E」。 %E

文字列の書式指定子

(17)

説明 指定子 64ビット浮動小数点数(double)。指数部が-4未満または精度以上であれば%eと同 じ、そうでなければ%fと同じ。 %g 64ビット浮動小数点数(double)。指数部が-4未満または精度以上であれば%Eと同 じ、そうでなければ%fと同じ。 %G

8ビット符号なし文字 (unsigned char)。NSLog()でASCII文字として出力。非ASCII

文字は8進形式 (\\ddd) またはUnicodeの16進形式 (\\udddd、但しdは数字)。 %c

16ビットUnicode文字 (unichar)。NSLog()でASCII文字として出力。非ASCII文字は8

進形式 (\\ddd) またはUnicodeの16進形式 (\\udddd、但しdは数字)。 %C 8ビット符号なし文字の配列(末尾はNull)。%s指定子があると、システムデフォル トのエンコーディングとして文字を解釈するので、特に書字方向が右から左(RTL、 Right-To-Left)の言語の場合、結果が変わる可能性があります。たとえば強い方向付 けがない文字の場合%s、書字方向マーカが挿入されるのです。したがって、%sの使 用を避け、エンコーディングを明示的に指定する方がよいでしょう。 %s 16ビットUnicode文字の配列(末尾はNull)。 %S voidポインタ(void *)。先頭に「0x」を置いた16進表示。数字(0-9)および英 小文字(a-f)で表記。 %p 64ビット浮動小数点数(double)の指数表記。先頭に「0x」、小数点の前に16進数 字を1つ、指数部の先頭は小文字の「p」。 %a 64ビット浮動小数点数(double)の指数表記。先頭に「0X」、小数点の前に16進数 字を1つ、指数部の先頭は大文字の「P」。 %A 64ビット浮動小数点数(double)の10進表記。 %F 表 2 NSStringやCFStringの整形メソッド/関数に指定できる長さ修飾子 説明 長さ修飾子

変換指定子d、o、u、x、Xの前に置いて、引数がshortまたはunsigned short

であることを指定。

h

変換指定子d、o、u、x、Xの前に置いて、引数がsigned charまたはunsigned charであることを指定。

hh

変換指定子d、o、u、x、Xの前に置いて、引数がlongまたはunsigned longで

あることを指定。

l

変換指定子d、o、u、x、Xの前に置いて、引数がlong longまたはunsigned long longであることを指定。

ll、q

文字列の書式指定子 書式指定子

(18)

説明 長さ修飾子 変換指定子a、A、e、E、f、F、g、Gの前に置いて、引数がlong doubleである ことを指定。 L 変換指定子d、o、u、x、Xの前に置いて、引数がsize_tまたはこれに類する符 号つき整数であることを指定。 z 変換指定子d、o、u、x、Xの前に置いて、引数がptrdiff_tまたはこれに類する 符号なし整数であることを指定。 t 変換指定子d、o、u、x、Xの前に置いて、引数がintmax_tまたはuintmax_tで あることを指定。 j

プラットフォームによる違い

OS Xは、NSInteger、NSUInteger、CGFloat、CFIndexなどさまざまなデータ型を使っており、32

ビット環境、64ビット環境それぞれにおいて、値の意味は一貫しています。32ビット環境では、

NSIntegerとNSUIntegerはそれぞれ、int型とunsigned int型を表します。一方、64ビット環境の

場合、NSIntegerとNSUIntegerはそれぞれ、long型とunsigned long型を表します。プラットフォー

ムが変わるとprintf方式の型指定子が違ってしまうのに対処するため、表 3のように指定するとよいで しょう。なお、値をキャストしなければならないこともあります。 表 3 データ型に応じた書式指定子 考慮点 書式指定子 型 値をlongにキャスト。 %ldまたは%lx NSInteger 値をunsigned longにキャスト。 %luまたは%lx NSUInteger 整形の場合、%fはfloat、doubleの両方に有効。ただし、書 式指定に従って文字列を解析するときは、下記の注意点を 参照。 %fまたは%g CGFloat NSIntegerと同様。 %ldまたは%lx CFIndex %pならば先頭に「0x」と印字。これが不要であれば%zxを 指定し、型のキャストはしない。 %pまたは%zx ポインタ NSInteger型の値にキャストを施して%ldを適用する例を示します。 文字列の書式指定子 プラットフォームによる違い

(19)

NSInteger i = 42;

printf("%ld\n", (long)i);

表 3に示した考慮点以外にも、書式指定に従って文字列を解析する場合、考えなければならない点が あります。すなわち、この場合、floatとdoubleを区別しなければなりません。floatならば%f、double

ならば%lfを指定してください。また、scanf(またはその系列)で、変換結果をCGFloat型変数に取

り込みたい場合は、いったんdouble型の変数に取り込んだ後、double型変数にCGFloatをコピーし

てください。 CGFloat imageWidth; double tmp; sscanf (str, "%lf", &tmp); imageWidth = tmp; 重要なのは、32ビット/64ビット環境のどちらでも、%lfがCGFloat型変数を正しく表さないことで す。%ldがどちらの環境でもlongに正しく適用できるのとは状況が違います。 文字列の書式指定子 プラットフォームによる違い

(20)

ファイルから、あるいはURLを指定して、文字列をNSStringに読み込む処理は、エンコーディングが 分かっていれば何の問題もありませんが、不明な場合はいろいろと難しい点があります。一方、書き 出しの場合は、エンコーディングの指定が必須です(効率のため、可能な限りURLを使ってくださ い)。

ファイルやURLから読み込む

NSStringにはファイルやURLを指定してデータを読み込む各種のメソッドがあります。一般に、エン コーディングが分かっていれば、データの読み込みは容易です。一方、プレーンテキストであること しか分からなければ、その時点で問題をひとつ抱えていることになります。できる限りこのような状 況は回避しなければなりません。プレーンテキストファイルを対象とする関数/メソッドは、エンコー ディングの指定が必須です(UTF-8やUTF-16+BOMであれば理想的)。

エンコーディングが分かっているデータを読み込む

エンコーディングが分かっている場合、次の例のように、 stringWithContentsOfFile:encoding:error:、stringWithContentsOfURL:encoding:error:、 あるいはinit...系のメソッドで読み込むことができます。 NSURL *URL = ...; NSError *error;

NSString *stringFromFileAtURL = [[NSString alloc]

initWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:&error];

if (stringFromFileAtURL == nil) { // an error occurred

NSLog(@"Error reading file at %@\n%@",

URL, [error localizedFailureReason]); // implementation continues ...

(21)

次の例のように、データオブジェクトを使って文字列を初期化することも可能です。この場合もエン コーディングを正しく指定しなければなりません。

NSURL *URL = ...;

NSData *data = [NSData dataWithContentsOfURL:URL];

// Assuming data is in UTF8.

NSString *string = [NSString stringWithUTF8String:[data bytes]];

// if data is in another encoding, for example ISO-8859-1 NSString *string = [[NSString alloc]

initWithData:data encoding: NSISOLatin1StringEncoding];

エンコーディングが未知のデータを読み込む

エンコーディングが未知のテキストを扱う場合、エラー発生時の対処法があるかどうか確認しておく とよいでしょう。たとえば、Appleの「Mail」や「Safari」にはエンコーディングを指定するメニュー、 「TextEdit」には明示的にエンコーディングを指定してファイルを開き直す機能があります。 エンコーディングを推測せざるを得ない場合(明示的に情報が与えられなければ推測 は不可避)、次 のようにするとよいでしょう。 1. stringWithContentsOfFile:usedEncoding:error:または initWithContentsOfFile:usedEncoding:error:(あるいはURLを指定して呼び出す同等のメ ソッド)を実行してみてください。 このメソッドはエンコーディングの判定を試み、成功した場合は参照渡しでそのエンコーディン グを返します。 2. (1)に失敗した場合、UTF-8を指定して試します。 3. (2)にも失敗した場合は、何らかの適当なエンコーディングを試します。 「適当な」の意味は状況にも依存します。C文字列のデフォルトエンコーディング、ISO、Windows Latin 1など、データの供給元に応じて判断してください。

4. 最後に、Application Kitに付属する、NSAttributedStringの読み込みメソッド

(initWithURL:options:documentAttributes:error:など)を試してください。 これはプレーンテキストファイルの読み込みを試み、実際に用いたエンコーディングを返しま す。ほぼあらゆるテキスト文書を対象とすることができるので、テキストに関する前提知識が何 もない場合は試してみる価値があるでしょう。もっとも、自然言語ではない、Foundationレベル のツールや文書には向かないかも知れません。 ファイルやURLに対する文字列の読み書き ファイルやURLから読み込む

(22)

ファイルやURLに書き出す

データの書き出しは、読み込みに比べれば分かりやすいでしょう。NSStringには、 writeToFile:atomically:encoding:error:およびwriteToURL:atomically:encoding:error:と いう便利なメソッドがあります。どちらの場合も、エンコーディングと、アトミックに書き込むか否 かを指定しなければなりません。アトミックでないとした場合、文字列は指定したパスに直接書き込 まれます。アトミックであると指定すれば、補助ファイルにいったん書き込んだ後、ファイル名を変 更するようになります。こうすれば、既存のファイルに上書きする際、書き込み中にシステムがク ラッシュしても、元のファイルは破損せずに残ります。ただし、URLを指定して書き込む場合、書き 込み先にアトミックにアクセスできなければこの指定は無視されます。 NSURL *URL = ...; NSString *string = ...; NSError *error;

BOOL ok = [string writeToURL:URL atomically:YES

encoding:NSUnicodeStringEncoding error:&error]; if (!ok) {

// an error occurred

NSLog(@"Error writing file at %@\n%@",

path, [error localizedFailureReason]); // implementation continues ...

まとめ

ファイルやURLを対象として文字列オブジェクトを読み書きする、よく使われる方法を表にまとめま す。 書き出し 生成方法 読み込み元/書き出し 先 writeToURL: atomically:encoding: error: stringWithContentsOfURL: encoding:error: stringWithContentsOfURL: usedEncoding:error: URLの内容 writeToFile: atomically:encoding: error: stringWithContentsOfFile: encoding:error: stringWithContentsOfFile: ファイルの内容 ファイルやURLに対する文字列の読み書き ファイルやURLに書き出す

(23)

文字列クラスには、文字列中の文字や部分文字列を検索する、あるいは他の文字列と比較するための メソッドがあります。いずれもUnicode規格に従って、2つの文字の並びが同等か否かを判定するよう になっています。文字列クラスの比較メソッドは結合文字列を適切に扱いますが、処理効率を重視す る場合、結合文字列が何らかの標準形になっていると保証できるならば、(結合文字列を考慮しな い)リテラル検索を指定しても構いません。

検索/比較メソッド

検索/比較メソッドにはさまざまな変種があります。最も単純なのは、文字列全体を対象とするもの です。それ以外に、結合文字列の比較方法を変更できる、文字列中のある範囲を指定して検索/比較 する、ロケールを指定して検索/比較する、などの変種もあります。 以下に基本的な検索/比較メソッドを示します。 比較メソッド 検索メソッド compare: rangeOfString: compare:options: rangeOfString: options: compare:options: range: rangeOfString: options:range: compare:options: range:locale: rangeOfString: options:range: locale:

rangeOfCharacterFromSet: rangeOfCharacterFromSet: options: rangeOfCharacterFromSet: options:range:

文字列を検索する

rangeOfString:...系のメソッドで、レシーバである文字列中の部分文字列を検索できます。 rangeOfCharacterFromSet:...系のメソッドは、文字の集合を指定して、そのいずれかの文字を検 索します。

文字列の検索、比較、整列

(24)

部分文字列が見つかるのは、指定した範囲内に、完全に当該文字列が含まれている場合に限ります。 NSLiteralSearch(下記参照)を指定した場合を除き、検索/比較メソッドに指定する範囲が、結合 文字列を分断するようなものであってはなりません。分断する場合、誤った結果になる可能性があり ます(分断しないよう範囲を調整するコード例が、rangeOfComposedCharacterSequenceAtIndex: メソッドを説明している節に載っています)。 また、NSScannerのインスタンスを使うと、文字列オブジェクトを走査し、数値や文字列値を抽出で きます。字句解析器については、“字句解析器” (37 ページ)を参照してください。NSString、 NSScannerのいずれも、NSCharacterSetクラスクラスタを用いる検索が可能です。文字集合につい ては“文字集合” (34 ページ)を参照してください。 あるパターンに合致する箇所が文字列中にあるかどうか判定したいだけであれば、術語を利用すると よいでしょう。

BOOL match = [myPredicate evaluateWithObject:myString];

術語について詳しくは、『Predicate Programming Guide 』を参照してください。

文字列を比較、整列する

compare:...系のメソッドは、レシーバである文字列と引数として指定した文字列を比較し、辞書的 順序を返します。ほかに、一方の文字列がもう一方に完全一致/前方一致/後方一致しているかどうか を判定するメソッドもありますが、これに検索オプションや範囲を指定できる変種はありません。 文字列を比較する最も単純なメソッドはcompare:でしょう。これは、オプションを指定せず、文字 列全体を比較範囲として、compare:options:range:メソッドを呼び出すのと同等です。比較オプ

ション(NSCaseInsensitiveSearch、NSLiteralSearch、NSNumericSearch)を指定する場合は compare:options:メソッド、さらにロケールも指定する場合はcompare:options:range:locale: メソッドを使います。NSStringにはさらに、範囲やオプションを指定しなくても一般的な比較がで きる、caseInsensitiveCompare:、localizedCompare:などのメソッドもあります。 Important: ユーザ向けに表示する目的で文字列を整列する場合は、必ず ローカライズを考慮して 比較してください。したがって通常、compare:やcaseInsensitiveCompare:の代わりに、 localizedCompare:やlocalizedCaseInsensitiveCompare:を使うことになります。 Finderに現れるのと同じ順序に並ぶよう文字列を比較するためには、compare:options:range:locale: メソッドを用い、ユーザのロケールのほか、オプションとしてNSCaseInsensitiveSearch,

NSNumericSearch、NSWidthInsensitiveSearch、NSForcedOrderingSearchを指定してください。

“Finderに現れるのと同じ順序に整列する” (27 ページ)にその例が載っています。

文字列の検索、比較、整列 検索/比較メソッド

(25)

検索/比較のオプション

検索/比較メソッドの中には、引数「options」を指定できるものがあります。これはビットマスクの 形になっており、必要に応じていくつでも指定できます。以下のオプションを組み合わせてマスクを 生成してください(メソッドによっては、指定しても何の効果もないオプションがあります)。 効果 検索オプション 大文字と小文字を区別しません。 NSCaseInsensitive-Search バイト単位で比較します。(結合文字列などの規則によって)同等 と看做されるものであっても、バイト列として違いがあれば異なる 文字列として扱います。状況によっては処理速度が劇的に向上しま す。 NSLiteralSearch 範囲の末尾から先頭に向かって検索します。 NSBackwardsSearch 範囲の先頭(NSBackwardsSearchも指定した場合は末尾)のみを検 索の対象とします。先頭/末尾で見つからなければ、文字列中のほか の位置にあっても、見つからなかったことになります。 NSAnchoredSearch compare:options:メソッドに指定すれば、比較の際、数字の並びを 数値として扱うようになります。たとえば、Filename9.txt< Filename20.txt<Filename100.txtとなります。 NSNumericSearch 現在は、検索や比較を、NSLiteralSearchオプションが指定されているものとして実行するように なっています。

大文字と小文字を区別しない、前方一致/後方一致の検索

NSStringにはhasPrefix:およびhasSuffix:というメソッドがあり、先頭/末尾部分の完全 一致によ

る検索をすることができます。rangeOfString:options:にオプションを指定して、大文字と小文字

を区別しない 検索を行う例を示します。

NSString *searchString = @"age";

NSString *beginsTest = @"Agencies";

NSRange prefixRange = [beginsTest rangeOfString:searchString 文字列の検索、比較、整列

(26)

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:)で、大文字と

小文字を区別しない比較ができます。

文字列の検索、比較、整列 例

(27)

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)

// equivalent to [string_a compare:string_A options:NSCaseInsensitiveSearch]

Finderに現れるのと同じ順序に整列する

OS X v10.6以降のFinderに現れるのと同じ順序に文字列を整列する場合、localizedStandardCompare: メソッドで比較します。リストや表に、ファイル名その他の文字列を、Finderと同じような順序で並 べたい場合に利用してください。具体的な動作はローカライズの状況によっても違うので、クライア ント側で、特定の整列順序になることを前提とした処理をしてはなりません。 以下の例では、Finderと同じ規則で比較する処理を別の方法で実装した後、文字列の配列を整列する 方法を示します。まず、比較オプションを適切に設定した整列関数を定義します(便宜上、ロケール はコンテキストとして指定)。

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:に渡します。ロケールはcontext:引数 で指定します。 文字列の検索、比較、整列 例

(28)

NSArray *stringsArray = @[@"string 1", @"String 21", @"string 12", @"String 11", @"String 02"];

NSArray *sortedArray = [stringsArray sortedArrayUsingFunction:finderSortWithLocale context:[NSLocale currentLocale]];

// sortedArray contains { "string 1", "String 02", "String 11", "string 12", "String 21" }

文字列の検索、比較、整列 例

(29)

この章では、単語や段落の区切りを定義する方法、改行を表示する方法、文字列を段落に区切る方法 を解説します。

単語の区切り

テキスト処理系が単語の区切りを判断する方法は、言語によって異なります。「Unicode Standard Annex #29」にもとづき、ロケールに応じて追加のカスタマイズを施してあります。OS Xの場合、Cocoa

には単語の区切りに関するAPIとして、NSAttributedStringのdoubleClickAtIndex:メソッド、

nextWordFromIndex:forward:メソッドがありますが、判定アルゴリズムそのものの方式を変えるこ

とはできません。

行や段落の区切り文字

行や段落の区切りを表す方法はいくつか考えられます。歴史的には、\n、\r、\r\nなどが使われて

きました。Unicodeには、明確に定義した段落区切り文字U+2029(Cocoaでは定数

NSParagraphSeparatorCharacterで参照可能)や行区切り文字U+2028(Cocoaでは定数 NSLineSeparatorCharacterで参照可能)があります。 Cocoaのテキスト処理系では、NSParagraphSeparatorCharacterは一貫して段落区切り、 NSLineSeparatorCharacterは一貫して段落区切りではない行区切り、すなわち段落の内部にある行 区切りとして扱われます。しかし他の環境で、このような取り扱いが保証されていることはほとんど ありません。たとえばPOSIXレベルのソフトウェアでは、多くの場合、\nが区切りと認識されるだけ です。旧Macintoshソフトウェアは\r、一部のWindowsソフトウェアは\r\nだけを区切りと認識しま す。行区切りと段落区切りの区別はありません。 適切な行/段落区切り文字は、データの使い方やプラットフォームに依存します。Cocoaのテキスト処 理系は、\n、\r、\r\nをいずれも、NSParagraphSeparatorCharacterと同等の段落区切り文字と認 識します。たとえばinsertNewline:メソッドで段落区切りを挿入する場合、実際に使うのは\nで す。NSLineSeparatorCharacterは元々、たとえばinsertLineBreak:メソッドやHTMLの<br>要素 で、段落区切りではなく行区切りのために使われていました。

単語、段落、改行

(30)

段落区切りではなく、特に行区切りを意図している場合、一般にNSLineSeparatorCharacterを用い

るべきです。そうでなければ、テキストを処理する他のソフトウェアに合わせて、\n、\r、\r\nの

いずれかを使ってください。Cocoaのデフォルト値は\nとなっています。

文字列を「段落ごとに」区切る

一般に、文字列を「段落ごとに」区切るには、次のようにするのが簡単です。

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:&paraStart end:&paraEnd

contentsEnd:&contentsEnd forRange:NSMakeRange(paraEnd, 0)]; currentRange = NSMakeRange(paraStart, contentsEnd - paraStart); [array addObject:[string substringWithRange:currentRange]]; }

単語、段落、改行

(31)

文字列は文字の並びであると考えるのが普通ですが、NSStringオブジェクトやUnicode文字列一般を 扱う場合、個々の文字ではなく部分文字列を対象として操作する方がよいでしょう。というのも、 ユーザがテキスト中の文字と認識するものが、文字列の中では複数の文字で表されていることも少な くないからです。NSStringにはUnicode文字列を適切に処理するためのメソッドが豊富に揃ってお り、容易にUnicode規格に沿った操作ができますが、いくつか注意事項があります。 NSStringオブジェクトは、概念的にはUTF-16で実装されています(エンディアンはプラットフォー ムに依存)。といっても、内部のストレージ機構がそうなっているとは限りません。NSStringの長

さ、文字インデックス、範囲はUTF-16の単位で表され、NSStringのメソッド名に現れる「character」

とはUTF-16における単位(16ビット長、プラットフォームに依存したエンディアン)である、という だけのことです。これは文字列オブジェクトに関する一般的な取り決めです。多くの場合、クライア ント側は特別に意識しなくてもよいでしょう。部分文字列を扱っている限り、範囲やインデックスの 厳密な解釈は、それほど重要ではありません。 現用言語の記述に用いる、Unicodeの大部分のコードポイントは、単一のUTF-16の単位で表されます。 しかし、あまり一般的でないコードポイントの中には、代用対(surrogate pair)で表されるものがあ ります。これは所定の範囲から選んだUTF-16の単位を2つ並べたもので、組になって単一のUnicode コードポイントを表します。CFStringには、代用対と、対応するUnicodeコードポイントのUTF-32表現 とを、相互に変換する関数があります。NSStringオブジェクトを扱う際の制約のひとつに、部分文 字列の境界が代用対を分断してはならない、という規則があります。Cocoaの大部分のメソッドが返 す範囲は自動的にこの制約を満たすようになっていますが、部分文字列の範囲を自分で計算して組み 立てる場合は、頭に入れておく必要があります。しかし考慮するべき制約は、これだけではありませ ん。 多くの書記体系で、ひとつの文字を、基底となる文字にアクセント記号その他の装飾を付け加える形 で構成することがあります。文字もアクセント類も数が多いので、Unicodeではその組み合わせすべ てにコードポイントを与えず、基底文字(base character)の後に結合記号(combining mark)をいく つか並べて表現するようにしています。互換性を考慮して、一般的な組み合わせには独立したコード ポイントを与えています。これを合成済み文字(precomposed character)と言います。Unicode正規化 変換により、合成済み文字とその分解表現(decomposed representation)とを相互に変換できます。 しかし、文字列を可能な限り合成済み文字で構成しようとしても、基底文字と結合記号でしか表現で きない組み合わせは多数あります。多くのテキスト処理では、部分文字列の範囲を調整して、その境 界が基底文字と結合記号を分断しないようにしなければなりません。

文字と書記素クラスタ

(32)

さらに、書記体系によっては、アクセント記号よりも複雑な部品を組み合わせて表現する文字もあり ます。たとえば韓国語の場合、ハングルのひとつの音節文字が、「字母(jamo)」と呼ばれる2つか 3つの部分から成り立っていることもあります。南アジアから東南アジアにかけて広がる、インド語 派およびその影響下にある書記体系では、ひとつの文字が、子音、母音、ビラーマその他の記号の組 み合わせで表されます。Unicodeでは個々の部分にのみコードポイントを与えていることが多く、そ の場合、文字は複数のコードポイントから合成することになります。テキスト処理においては、部分 文字列の範囲を調整して、ハングルの音節文字中の字母や、インド語派の子音群(consonant cluster) の構成要素を分断しないようにしなければなりません。 一般に、代用対、基底文字と結合記号、ハングルの字母、インド語派の子音群などの組み合わせを、 書記素クラスタ(grapheme cluster)と呼びます。これを考慮して処理するためには、NSStringの rangeOfComposedCharacterSequencesForRange:メソッドや rangeOfComposedCharacterSequenceAtIndex:メソッド、あるいは CFStringGetRangeOfComposedCharactersAtIndexを使うとよいでしょう。文字列のインデックス や部分文字列の範囲を、書記素クラスタの境界に合わせて調整し、上記の制約が満たされるようにす ることができます。ユーザが認識する文字境界をプログラムで判定する必要があれば、まずこのメ ソッドが使えないか検討してください。: Unicodeのアルゴリズムはときに、書記素クラスタの境界をも越えるような形で、複数の文字を扱う ことがあります。たとえば小文字から大文字への変換アルゴリズムは、単一の文字を複数の文字に変 換することがあります。たとえばドイツ文字「ß」を大文字で表すと、「SS」という2つの文字の並び になります。また、言語によっては、文字照合アルゴリズムが、複数の文字の並びをひとつの単位と して扱うことがあります。たとえばヨーロッパの一部の言語では、整列順を決める際、「ch」を独立 した文字として扱います。このような場合でも適切に処理できるよう、大文字小文字変換、整列、検 索においては、NSStringの標準メソッドを文字列全体に適用することが重要です。NSStringメソッ

ドである、lowercaseString、uppercaseString、capitalizedString、compare:およびその変

種、rangeOfString:およびその変種、rangeOfCharacterFromSet:およびその変種、あるいはCFString

に定義された同等のメソッドを使ってください。Unicode文字列の複雑な処理をすべて考慮し、特に 検索や整列のメソッドは、何を「同じ文字」と看做すか、さまざまな選択肢を組み込んで実装してあ ります。

ごく稀に、特殊な使い方に合わせて、書記素クラスタの定義を微調整しなければならないことがあり ます。書記素クラスタの境界を判定、調整する際の問題点については、「Unicode Standard Annex #29」を参照してください。さまざまな例に加え、アルゴリズムもいくつか載っています。Unicode規 格は全般的に、Unicodeアルゴリズムや、Unicode文字列処理に関する検討事項についての、第一の情 報源になっています。 カーソル移動や挿入位置の処理という観点から書記素クラスタ境界について詳しく知りたい、あるい はCocoaのテキスト処理系を使いたい場合、OS X v10.5以降のNSLayoutManagerには、レイアウトした テキストの行内で、挿入位置を判定するためのAPIがあるので、参考にするとよいでしょう。なお、 文字と書記素クラスタ

(33)

挿入点境界とグリフ境界は、同じとは限りません。たとえばラテン文字の「fi」リガチャのように、 目に見える文字境界とは別の所に挿入点が必要になる場合があります。詳しくは『CocoaTextArchitecture

Guide 』を参照してください。 文字と書記素クラスタ

(34)

NSCharacterSetオブジェクトはUnicode文字の集合を表します。NSStringやNSScannerは NSCharacterSetオブジェクトを使って、検索対象である文字をグループ化し、この集合に属する任 意の文字を見つけられるようにしています。

文字集合に関する基本事項

文字集合オブジェクトは、Unicode文字の集合を表します。実際には、クラスクラスタのインスタン スで表すようになっています。クラスタに属する2つの公開クラス、NSCharacterSetおよび NSMutableCharacterSetはそれぞれ、不変および可変な文字集合に対するプログラムインターフェ イスを宣言しています。不変文字集合は生成時に定義され、後で変更することはできません。一方、 可変文字集合は生成後でも変更できます。 文字集合オブジェクト自身は何もタスクを実行しません。文字列に対する処理を制約するために用い る、文字値の集合を保持するだけです。NSStringおよびNSScannerクラスには、引数として NSCharacterSetオブジェクトを取り、ある文字集合に属する文字を検索するメソッドがあります。 たとえば次のコードは、myString:中の最初の英大文字を、範囲の形で求めます。

NSString *myString = @"some text in an NSString...";

NSCharacterSet *characterSet = [NSCharacterSet uppercaseLetterCharacterSet]; NSRange letterRange = [myString rangeOfCharacterFromSet:characterSet];

rangeOfCharacterFromSet:を実行した結果、letterRange.locationは、「NSString」の先頭にあ

る「N」という文字のインデックスに等しい値になります。文字列の先頭文字が「S」であったとすれ ば、letterRange.locationの値は0になるはずです。

文字集合を生成する

NSCharacterSetには、英字(大文字、小文字)、10進数字、空白など、よく使う文字集合を返すク ラスメソッドがあります。こういった「標準」の文字集合は、NSMutableCharacterSetにメッセー ジを送信して取得したとしても、常に不変です。標準文字集合については、“標準文字集合とUnicode 規格における定義” (36 ページ)を参照してください。

文字集合

(35)

標準文字集合を出発点として、その可変なコピーを作り、変更を施して、独自の文字集合を構築でき

ます(allocとinitで可変の文字集合を生成し、必要な文字を追加するという方法もあります)。た

とえば次のコードは、英数字や主な句読点から成る文字集合を生成します。

NSMutableCharacterSet *workingSet = [[NSCharacterSet alphanumericCharacterSet] mutableCopy];

[workingSet addCharactersInString:@";:,."];

NSCharacterSet *finalCharacterSet = [workingSet copy];

Unicodeのコードポイントで文字を指定して独自の文字集合を定義する場合は、次のように記述する とよいでしょう(これは用紙送り文字と行区切り文字から成る文字集合を生成する例)。

UniChar chars[] = {0x000C, 0x2028};

NSString *string = [[NSString alloc] initWithCharacters:chars

length:sizeof(chars) / sizeof(UniChar)]; NSCharacterSet *characterSet = [NSCharacterSet

characterSetWithCharactersInString:string];

処理性能に関する考慮事項

文字集合を扱うコードは処理性能のネックになることが多いので、性能に影響を与えうる要因を意識 しておかなければなりません。可変な文字集合は一般に、不変な文字集合よりも性能面で劣ります。 必要なメモリ容量が多く、(字句解析によく現れる)反転処理にも時間がかかります。次のガイドラ インに従って実装するとよいでしょう。 ● 可変な文字集合はできるだけ使わないでください。 ● 必要の都度生成し直すのではなく、(グローバル辞書などに)キャッシュしてください。 ● 独自の文字集合を生成し、その後変更する必要がない場合は、不変な文字集合を生成してコピー し、元の可変な文字集合は破棄するとよいでしょう。あるいは、文字集合のファイルを生成し (“文字集合ファイルを生成する” (36 ページ)を参照)、アプリケーションの主バンドルに格 納しておく、という方法もあります。 ● 同様に、文字集合オブジェクトをアーカイブすることも避け、代わりに文字集合ファイルに保存 してください。アーカイブすると、異なるアーカイブファイルに文字集合を複製することにな り、ディスク空間の無駄になるだけでなく、アーカイブを読み込む都度、メモリを消費すること になります。 文字集合 処理性能に関する考慮事項

(36)

文字集合ファイルを生成する

独自の文字集合を頻繁に使う場合、必要の都度個々の文字を追加して生成する代わりに、定義をリ ソースファイルに保存しておき、実行時に読み込んで使うとよいでしょう。文字集合は、そのビット マップ表現(NSDataオブジェクト)を取得し、ファイルに保存することができます。

NSData *charSetRep = [finalCharacterSet bitmapRepresentation]; NSURL *dataURL = <#URL for character set#>;

NSError *error;

BOOL result = [charSetRep writeToURL:dataURL options:NSDataWritingAtomic error:&error]; 文字集合ファイルの拡張子は.bitmapとするのが慣習になっています。文字集合ファイルを他のユー ザも使えるようにしたい場合は、この慣習に従ってください。拡張子が.bitmapの文字集合ファイル は、characterSetWithContentsOfFile:メソッドで簡単に読み込めます。

標準文字集合とUnicode規格における定義

標準文字集合(letterCharacterSetの戻り値として得られるものなど)は、Unicode規格で、

「Uppercase Letter」、「Combining Mark」などが、規定(normative)または参考(informative)のカ テゴリとして正式に定義されています。標準文字集合の正式な定義は、ほとんどの場合、規格に定義 されたカテゴリに基づきます。たとえばlowercaseLetterCharacterSetの戻り値である文字集合に は、「規定」カテゴリである「Lowercase Letters」の文字がすべて含まれます。同様に、 letterCharacterSetの戻り値である文字集合は、「Letter」カテゴリの文字から成ります。 なお、カテゴリ自身の定義は、Unicode規格の版によって変わることがあります。カテゴリを定義し ているファイルは、http://www.unicode.org/からダウンロード可能です。 文字集合 文字集合ファイルを生成する

(37)

NSScannerオブジェクトはNSStringオブジェクト中の文字を解析し、数値や文字列値として変換し ます。解析対象の文字列を指定して生成した後、先頭から順次、要件に合致する文字を抽出、変換す るようになっています。

字句解析器を生成する

NSScannerはクラスクラスタで、その公開クラスはNSScannerです。一般にそのインスタンスは、ク ラスメソッドであるscannerWithString:またはlocalizedScannerWithString:で生成します。い ずれも指定した文字列を解析するよう初期化した字句解析器オブジェクトを返します。その後、字句 解析器は文字列の先頭から解析を始めます。scan...系列のメソッド(scanInt:、scanDouble:、

scanString:intoString:など)で、字句要素を取り出すことになります。複数の字句要素を抽出す

る場合は、whileループ内に組み込み、文字列の末尾まで繰り返すのが普通です(コード例を参照)。

float aFloat;

NSScanner *theScanner = [NSScanner scannerWithString:aString]; while ([theScanner isAtEnd] == NO) {

[theScanner scanFloat:&aFloat]; // implementation continues... } 解析の際、大文字と小文字を区別するかどうかは、setCaseSensitive:メソッドで設定可能です。 デフォルトでは区別しない設定になっています。

字句解析器の使い方

文字列の走査は、「現在」位置から始め、要件に合致する表現を見つけて、その最後の文字を通り過 ぎた位置まで進めます。たとえば、「137 small cases of bananas」という文字列から整数値を抽

出すると、現在位置は3、すなわち、数字直後の空白になります。解析対象外の文字を飛ばすため、 単に位置を進めたいこともよくあります。その場合、setScanLocation:メソッドで所定の位置まで

(38)

進める、という方法があります(このメソッドは、エラー時に戻って走査し直すためにも使えます)。 しかし通常は、ある文字集合に属する文字や指定した文字列を読み飛ばしたり、指定した文字列が現 れるまで位置を進めたりしたいことが多いでしょう。 ある文字集合に属する文字を読み飛ばしたい場合は、setCharactersToBeSkipped:メソッドを使い ます。処理に先立って該当する文字を読み飛ばし、それ以外の文字が見つかった後はすべて照合の対 象にします。デフォルトでは、空白と改行文字を読み飛ばすようになっています。なお、この指定で は常に大文字と小文字を区別することに注意してください。たとえば英語の母音文字を読み飛ばした い場合、「AEIOUaeiou」という形で指定しなければなりません。 現在位置以降、指定した文字列の手前までを抽出したい場合は、scanUpToString:intoString:メ ソッドを使います(第2引数としてNULLを指定すれば、指定した文字列まで単に読み飛ばすことにな ります)。たとえば次の文字列を考えましょう。

137 small cases of bananas

scanUpToString:intoString:を次のように使えば、容器の個数(137)と種類(small cases)を取

り出すことができます。

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"(先頭に空白なし)であったとすれば、containerの最初の値は「small cases 」(末尾

の空白を含む)となります。これに対し、" of"(先頭に空白あり)であったとすれば、結果は「small cases」(末尾に空白なし)containerとなります。 指定した文字列の手前までを抽出した(あるいは読み飛ばした)時点で、「現在」位置は、見つかっ た文字列の先頭になります。したがって、見つかった文字列の直後から続行したい場合は、この文字 列を読み飛ばさなければなりません。次のコード例では、検索文字列( of)までを読み飛ばした後、 字句解析器 字句解析器の使い方

(39)

その直後から末尾までを抽出して、容器に入っている商品(bananas)を調べています。

substringFromIndex:を使って、実質的に、検索文字列の末尾まで読み飛ばしていることに注意し

てください。

[aScanner scanString:separatorString intoString:NULL]; NSString *product;

product = [[aScanner string] substringFromIndex:[aScanner scanLocation]]; // could also use:

// 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

次の例では、各種の走査演算を使って、製品名と金額(簡単のためfloat型として読み取り)を抽出

します。部分文字列「Product:」や「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 *PRODUCT = @"Product:"; NSString *COST = @"Cost:";

NSString *productName; float productCost; NSInteger productSold; 字句解析器

Updating...