Skip to content

Objective-C 学习笔记(七)

Published: at 11:43 PM (8 min read)

NSFileManager

NSFileManager 类能够完成以下对于文件的操作

首先给出常见的 NSFileManager文件方法

attributesOfItemAtPath:path 方法返回一个包含指定文件属性的字典,属性字典包括各种信息,如文件的所有者、文件大小、文件的创建日期等。字典的每个属性可以通过键值提取,而所有的键都定义在头文件<Foundation/NSFileManager.h>中,如表示文件大小的键值为NSFileSize,以下列举了一部分常见属性。

   NSFileAttributeKey const NSFileType; :
   NSFileAttributeType const NSFileTypeDirectory;
   NSFileAttributeType const NSFileTypeRegular;
   NSFileAttributeKey const NSFileSize;
   NSFileAttributeKey const NSFileModificationDate;  //修改时间
   NSFileAttributeKey const NSFileCreationDate; //创建时间

使用以上方法在示例代码中实现了一些当前目录下的简单文件功能,须确保当前目录下 test.txt 已创建。

#import <Foundation/Foundation.h>

int main(int argc, char *argv[]){
    @autoreleasepool {
        NSString *fName = @"test.txt";
        NSString *nName = @"copy.txt";
        NSFileManager *fm;
        NSDictionary *attr;
        
        fm = [NSFileManager defaultManager];
        
        if([fm fileExistsAtPath:fName] == NO){
            NSLog(@"File does not exist!");
            return 1;
        }
        
        if([fm copyItemAtPath:fName toPath:nName error:NULL] == NO){
            NSLog(@"File copy failed!");
            return 2;
        }
        
        if ([fm contentsEqualAtPath:fName andPath:nName] == NO){
            NSLog(@"Files are not equal");
            return 3;
        }
        if([fm moveItemAtPath:nName toPath:@"new.txt" error:NULL] == NO){
            NSLog(@"File renamed failed");
            return 4;
        }
        
        if((attr = [fm attributesOfItemAtPath:fName error:NULL]) == nil){
            NSLog(@"Couldn't get file attributes!");
            return 5;
        }
        
        NSLog(@"File size is %llu bytes",[[attr objectForKey:NSFileSize] unsignedLongLongValue]);
        
        if([fm removeItemAtPath:fName error:NULL] == NO){
            NSLog(@"File removal failed");
            return 6;
        }
        
        NSLog(@"All operations success!");
    }
    return 0;
}

书中给出了常见的 NSFileManager 目录方法

moveItemAtPath:toPath:方法可以将文件从一个目录移到另一个目录中,如果两个路径引用同一目录中的文件,其结果为重命名这个文件。使用示例代码实现了部分目录操作如下所示:

#import <Foundation/Foundation.h>

int main(int argc, char *argv[]){
    @autoreleasepool {
        NSString *dname = @"testdir";
        NSString *path;
        NSFileManager *fm;
        
        fm = [NSFileManager defaultManager];
        
        path = [fm currentDirectoryPath];
        NSLog(@"Current Directory is: %@",path);
        
        if([fm createDirectoryAtPath:dname withIntermediateDirectories:YES attributes:nil error:NULL] == NO){
            NSLog(@"Couldn't create directory!");
            return 1;
        }
        
        if([fm moveItemAtPath:dname toPath:@"newdir" error:NULL] == NO){
            NSLog(@"Directory rename failed!");
            return 2;
        }
        
        if([fm changeCurrentDirectoryPath:@"newdir"] == NO){
            NSLog(@"Change directory failed!");
            return 3;
        }
        
        path = [fm currentDirectoryPath];;
        NSLog(@"Current Directory is: %@",path);
        
        NSLog(@"All operation completed.");
    }
    return 0;
}

NSData

在Foundation框架的 NSData 中提供了缓冲区的使用方式,包括设置缓冲区、数据读入缓冲区、将缓冲区数据写入文件等。 使用 NSFileManager 对象的 contentsAtPath: 方法能够接收一个路径名,并将指定文件内容读入该方法创建的存储区,若读取成功则返回存储区对象,否则返回nil。方法 createFileAtPath: contents: attributes: 创建特定属性的文件并将指定的 NSData 对象内容写入该文件中。

#import <Foundation/Foundation.h>

int main(int argc, char *argv[]){
    @autoreleasepool {
        NSFileManager *fm;
        NSData *data;
        
        data = [fm contentsAtPath:@"new.txt"];
        
        if(data == nil){
            NSLog(@"File read failed!");
            return 1;
        }
        
        if([fm createFileAtPath:@"another.txt" contents:data attributes:nil] == NO){
            NSLog(@"Couldn't create the copy!");
            return 2;
        }
        
        NSLog(@"File successfully copied.");
    }
    return 0;
}

使用enumeratorAtPath: 方法或者 contentsOfDirectoryAtPath:error: 方法都可以完成枚举过程。如果使用前者,一次可以枚举指定目录中的每个文件,默认情况下,如果其中一个文件为目录,那么也会递归枚举它的内容。

while ((path = [dirEnum nextObject]) != nil) (
NSLog (@"%@",path);
[fm fileExistsAtPath: path isDirectory: &flag];
if (flag == YES)
[dirEnum skipDescendents] ;
}

使用 isDirectory 方法检验文件是否为目录,通过发送 skipDescendents 消息可以动态组织递归过程,不再枚举目录中的内容。 下面的代码使用两种方式枚举指定目录中的内容。

#import <Foundation/Foundation.h>

int main(int argc, char *argv[]){
    @autoreleasepool {
        NSString *path;
        NSFileManager *fm;
        NSDirectoryEnumerator *dm;
        NSArray *array;
        
        fm = [NSFileManager defaultManager];
        
        path = [fm currentDirectoryPath];
        dm = [fm enumeratorAtPath:path];
        NSLog(@"Contents in current path.");
        
        //method 1
        while ((path = [dm nextObject]) != nil) {
            NSLog(@"%@",path);
        }
        
        //method 2
        array = [fm contentsOfDirectoryAtPath:[fm currentDirectoryPath] error:NULL];
        NSLog(@"Contents in current path.\n");
        for (path in array) {
            NSLog(@"%@",path);
        }
    }
    return 0;
}

###NSPathUtilities 书中给出了常见的 NSPathUtilities 路径方法表。其中 components 是一个 NSArray 对象,包含路径每一部分的字符串对象;path是一个字符串对象,指定文件的路径; ext 是路径扩展名的字符串对象。 以下为一段简单的 NSPathUtilities Demo代码:

#import <Foundation/Foundation.h>

int main(int argc, char *argv[]){
    @autoreleasepool {
        NSString *path,*temp,*home;
        NSFileManager *fm;
        NSArray *components;
 
        fm = [NSFileManager defaultManager];
        
        path = [fm currentDirectoryPath];
        NSLog(@"Current directory:%@",path);
        
        temp = NSTemporaryDirectory();
        NSLog(@"Temporary directory: %@",temp);
        
        home = NSHomeDirectory();
        NSLog(@"Home directory: %@",home);
        components = [home pathComponents];
        
        for (path in components) {
            NSLog(@"%@",path);
        }
    }
    return 0;
}

NSFileHandle

使用 NSFileHandle 方法可以实现如下操作

处理文件的一般步骤为

  1. 打开文件,并获取一个NSFileHandle对象
  2. 对打开的文件执行I/O操作
  3. 关闭文件

下图中给出了部分常用的NSFileHandle方法 以下为一段简单的 NSFileHandle Demo代码:

#import <Foundation/Foundation.h>

int main(int argc, char *argv[]){
    @autoreleasepool {
        NSFileHandle *inFile,*outFile;
        NSData *data;
        
        inFile = [NSFileHandle fileHandleForReadingAtPath:@"test.txt"];
        if(inFile == nil){
            NSLog(@"Open input file failed.");
            return 1;
        }
        
        outFile = [NSFileHandle fileHandleForWritingAtPath:@"out.txt"];
        if(outFile == nil){
            NSLog(@"Open output file failed");
            return 2;
        }
        
        [outFile seekToEndOfFile];
        
        data = [inFile readDataToEndOfFile];
        [outFile writeData: data];
        
        [inFile closeFile];
        [outFile closeFile];
        
        NSLog(@"%@",[NSString stringWithContentsOfFile:@"out.txt" encoding:NSUTF8StringEncoding error:NULL]);
    }
    return 0;
}

从输出可知,第一个文件的内容成功地附加到第二个文件的末尾。 若 seekToEndOfFile 方法到达文件的末尾并且没有读到任何数据,那么将返回一个空的 NSData 对象,通过对该 NSData 对象应用 length 方法,测试其长度是否等于零判断文件是否为空,或者查看该文件中是否还有数据可以读取。 打开一个需要更新的文件,文件的偏移量应设为文件的开始。通过在文件中定位(seeking)可以更改偏移量,然后执行该文件的读写操作。因此,要定位到文件的第10字节,可以编写如下消息表达式,此时文件的句柄为 databaseHandle。

[databaseHandle seekToFileOffset: 10];

通过获得当前文件的偏移量,然后加上或者减去这个值,就得到相应文件的位置。 跳过文件中当前位置之后的128字节需要使用如下代码:

[databaseHandle seekToFileOffset:[databaseHandle offsetInFile] + 128];

其他

书本中列出了部分 iOS 常用目录,内容略微过时但仍有参考意义。

参考

iOS中的文件管理(一)—— NSFileManager基础 Objective-C 程序设计 (第六版)