Objective-C ファイル操作 NSFileManager 【初級編 第24回】

この記事は2013年3月5日に書かれたものです。内容が古い可能性がありますのでご注意ください。


NSFileManagerによるファイル操作

ファイル操作

前回、前々回とNSStringクラスを使用したObjective-Cでのファイルの読み込み、書き込みを行いました。
そのときファイル名をソース内で指定していましたが、あるディレクトリ内のファイル一覧からデータを
読み込みしたいといった場合はディレクトリを検索するという操作が必要になります。
今回はこういったファイルの読み書き以外の操作について紹介したいと思います。

NSFileManagerクラス

Objective-Cでディレクトリ内のファイル一覧を取得したいといった場合や、ファイルのコピーなどといった
ファイルの一般的な操作をするときに使用されるクラスがNSFileManagerクラスとなります。
このNSFileManagerクラスにはディレクトリの検索やファイルのコピー用のメソッドなどが用意されています。

このクラスを使用するにはまずオブジェクトを作成する必要があります。
オブジェクト化するには「defaultManager」メソッドを使用します。
ただしこのdefaultManagerを使用してインスタンス化した場合は、常に同じNSFileManagerオブジェクトが返される
形となります。アプリケーション全体で使用されるオブジェクトとなります。
GNUStepの「NSFileManager.m」ファイル内にこのメソッドについて以下のように記載されています。
Returns a shared default file manager which may be used throughout an application.
アップルのリファレンスにも「This method always returns the same file manager object.」と記載がありますね。

「defaultManger」メソッドは「NSFileManager.h」内で以下のように定義されています。

+ (NSFileManager*) defaultManager

返り値がNSFileManagerオブジェクトへのポインタとなっています。

オブジェクトの作成ができたら、次にディレクトリ一覧を取得するためのメソッド「contentsOfDirectoryAtPath」や「subpathsAtPath」を見てみます。(これ以外にもあります)アップルのリファレンスを見ていただくとわかりますが、
OSのバージョンによっては使用できなくなるものがあります。
例えばディレクトリ一覧を取得する場合だと「directoryContentsAtPath」がMac OS X v10.5 から使用できなくなります。
「contentsOfDirectoryAtPath」メソッドは「NSFileMamnager.h」内で定義されています。

- (NSArray*) contentsOfDirectoryAtPath: (NSString*)path error: (NSError**)error;

第1引数は一覧取得したいディレクトリを指定します。
第2引数はNSErrorオブジェクトを指定します。エラーの情報を管理するために使用されるクラスとなります。
前回、前々回も出てきましたが「*」が2つついているのでポインタのポインタということになります。
戻り値は配列でかえってきます。
配列から値を取り出す方法はObjective-C NSArray 配列 【初級編 第20回】を参考にしてください。
このメソッドを使用した場合、検索したディレクトリのサブディレクトリ名は取得できますが、その中のファイルまでは
検索できません。

「subpathsAtPath」メソッドは「NSFileMamnager.h」内で定義されています。

- (NSArray*) subpathsAtPath: (NSString*)path

第1引数は一覧取得したいディレクトリを指定します。
戻り値は配列でかえってきます。
「contentsOfDirectoryAtPath」メソッドと違い、サブディレクトリ内のファイルまで検索できます。

コードでの確認

では実際に実装してみましょう。今回は実行ファイルの配置されるディレクトリに「data」というディレクトリを作成し
その配下に「data201301.txt」「data201302.txt」を配置しそこにユーザの身長や体重のデータが書き込まれているとします。
「main.m」でファイルを読み込んでいたのでその部分を改良することにします。
今回は「contentsOfDirectoryAtPath」メソッドを使用することにします。

main.m

#import <Foundation/Foundation.h>
#import <Foundation/NSObject.h>
#import "Bmi.h"
#import "Person.h"
#import "Manage.h"

int main(int argc, const char *argv[]){

    Person *person = nil;
    int i,j;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSError *error=nil;
    NSString *path = @"./data/";

    Manage *manage = [[Manage alloc] init];

    NSFileManager *filemanager = [ NSFileManager defaultManager];
    NSArray *files = [filemanager contentsOfDirectoryAtPath:path error:&error];
    for( i = 0; i < [files count]; i++){
        NSString *filepath = [path stringByAppendingString:[files objectAtIndex:i]];
        printf("file --%s--n",[filepath UTF8String]);
        NSString *file = [NSString stringWithContentsOfFile:filepath
                                encoding:NSUTF8StringEncoding
                                error:&error ];

        NSArray *line = [file componentsSeparatedByString:@"n"];
        for(j = 0; j < [line count]; j++){
            if( [[line objectAtIndex:j] isEqualToString:@""]) {
                continue;
            }
            NSArray *col = [[line objectAtIndex:j] componentsSeparatedByString:@" "];
            person = [[Person alloc] initWithWeight:[[col objectAtIndex:1] doubleValue]
                                 height:[[col objectAtIndex:2] doubleValue] name:[col objectAtIndex:0]];
            [manage setPerson:person];
            [person release];
        }
    }
    [manage showBestPerson];
    [manage recordBestPerson];
    [manage release];
    [pool drain];
    return 0;
}

13行目で検索するディレクトリ位置のパスを文字列定数として作成しています。

17行目でNSFileManagerオブジェクトを作成しています。

18行目でpathで指定したディレクトリ内のファイル一覧を取得しています。配列filesに取得したファイル名が格納されます。
今回の場合、「data201301.txt」と「data201302.txt」が格納されているはずです。このとき取得しているのはあくまで
ファイル名となります。フルパスではないということになります。

19行目は取得したファイルの数だけ繰り返し処理をするようにしています。まず最初のファイルからデータを取り出して
各種処理を行い、終了すると次のファイルを読み込むという処理になります。

20行目では上でも述べたように18行目の処理ではファイル名だけが取得できるため、そのままではカレントディレクトリにある
ファイルという扱いになってしまいます。そこでpathと文字列結合してファイルのパスを作成しています。

21行目はファイル名がとれているのかを確認するために入れています。

22行目は前回と同じで指定されたファイルの内容を読み込んでいます。
後の部分は前回と同じです。

読み込むデータには以下の内容が記録されています。
Objc24-2

このデータの中では「Ueda」が最も標準に近い値となるはずです。

結果確認

結果を見てみましょう。
Objc24-1

BMIの結果の間に、画面に読み込んだファイルの相対パスが表示されていますね。
読み込んだ結果が反映されていると思います。

このようにディレクトリ内を検索するといった用途に「NSFileManager」クラスが利用できます。

まとめ

今回のまとめとしては
・ディレクトリの検索やファイルのコピーなどは「NSFileManager」クラスを使用することでできる
・「NSFileManager」クラスをオブジェクト化するにはdefaultManagerオブジェクトを使用する方法があり
アプリケーション共通のオブジェクトができる(別のところでオブジェクト化しても同じオブジェクトを参照する)
・ディレクトリを検索するには「contentsOfDirectoryAtPath」や「subpathsAtPath」といったメソッドが使用できるが
少しだけ挙動が違う
といったところでしょうか。

  • このエントリーをはてなブックマークに追加

PAGE TOP