数据处理之文件读写

原文

沙盒机制

沙盒的概念:沙盒是每一个iOS应用程序都会自动创建的一个文件系统目录(文件夹),而且沙盒还具有独立、封闭、安全的特点。

  • 沙盒机制
  • iOS中的沙盒不仅仅是一个文件目录,TA其实更是一种安全体系
  • TA规定了应用程序只能在为该应用程序创建的文件夹(也就是沙盒)内访问文件,不可以访问其他沙盒内的内容(iOS已经部分开放访问)
  • 所有的非代码文件都保存在沙盒中,图片、音频、视频、属性列表(plist)、sqlite数据库以及文本文件等。
  • 沙盒机制的特点
  • 每个应用程序的活动范围都限定在自己的沙盒里
  • 不能随意跨越自己的沙盒去访问别的应用程序沙盒的内容(iOS8已经部分开放访问)
  • 应用程序想外请求或者接手数据都需要经过权限认证
  • 沙盒文件系统目录
    获取某个模拟器下某个应用程序沙盒的所在位置

/Users/用户名/Library/Developer/CoreSimulator/Devices/6892D97C-37CD-4788-9EAF-DD65C0C5480C/data/Containers/Data/Application/85F0B633-0C22-4511-BCD8-3C531D9C9750

  • 上面的文件路径就是一个应用程序的沙盒所在位置,也就是沙盒的根目录
  • 第一个粗体显示的路径是设备(模拟器)的路径,第二个粗体显示的路径表示这个应用程序沙盒的路径
  • 打开某个应用程序的沙盒
  • 三击你的沙盒路径(选中区域前后有多余的文字,并不会干扰接下来的操作,Mac系统会自动识别其中的文件路径)
  • 点击鼠标右键选择Services
  • 在Services的子选项中选择Reveal in Finder
  • 应用程序的沙盒目录
    应用程序的沙盒目录下拥有三个很特殊的文件夹分别是DocumentsLibrary(拥有Caches和Preferences目录)tmp

Documents:保存应用程序运行时生成的需要持久化数据,iTunes会自动备份该目录
苹果公司建议将程序中建立的活在程序浏览到的文件数据保存在该目录下,iTunes备份和恢复的时候会包括此目录
Library:存储程序的默认设置和其他状态信息,iTunes会自动备份该文件目录
Library/Caches:存放缓存文件,iTunes不会备份此目录,此目录下文件不会在应用退出时删除,通常存放体积比较大,但并不是很重要的资源
Library/Preferences:保存应用的所有偏好设置,iOS的Setting(设置)应用会在该目录中查找应用的设置信息,iTunes会自动备份该目录。——PS:如果你想对偏好设置进行相应的操作,应该使用NSUserDefaults类来取得和设置应用程序的偏好,而不是直接创建偏好设置文件
tmp:保存应用程序运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除,应用没有运行时,系统也有可能会清除该目录下的文件,iTunes不会同步该目录,你的iPhone重启时,该目录下的文件会被删除

  • 获取沙盒内文件的路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 获取Documents目录
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// 获取tmp目录
NSString *tmpPath = NSTemporaryDirectory();
// 获取Library目录
NSString *libPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
// 获取Library/Caches目录
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
// 获取Library/Preferences目录
NSString *prePath = [NSSearchPathForDirectoriesInDomains(NSPreferencePanesDirectory, NSUserDomainMask, YES) lastObject];
//通常情况下,Preferences由系统维护,我们很少去操作TA
// 获取应用程序包的路径
NSString *path = [NSBundle mainBundle].resourcePath;

简单对象的读写(I/O)操作
NSString(字符串)
NSArray(数组)
NSDictionary(字典)
NSData(数据)
包括上述类型的子类

  • NSString的写入与读取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 字符串写入沙盒
    // 在Documents下面创建一个文本路径,假设文本名称为objc.txt
    NSString *txtPath = [docPath stringByAppendingPathComponent:@"objc.txt"]; // 此时仅存在路径,文件并没有真实存在
    NSString *string = @"Objective-C";
    // 字符串写入时执行的方法
    [string writeToFile:txtPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
    NSLog(@"txtPath is %@", txtPath);
    // 字符串读取的方法
    NSString *resultStr = [NSString stringWithContentsOfFile:txtPath encoding:NSUTF8StringEncoding error:nil];
    NSLog(@"resultStr is %@", resultStr);
  • NSArray的写入与读取

1
2
3
4
5
6
7
8
9
10
11
// 数组写入文件
// 创建一个存储数组的文件路径
NSString *filePath = [docPath stringByAppendingPathComponent:@"language.txt"];
NSArray *array = @[@"C语言", @"JAVA",@"Objective-C", @"Swift", @"PHP", @"C++", @"C#"];
// 数组写入文件执行的方法
[array writeToFile:filePath atomically:YES];
NSLog(@"filePath is %@", filePath);
// 从文件中读取数据数组的方法
NSArray *resultArr = [NSArray arrayWithContentsOfFile:filePath];
NSLog(@"%@", resultArr[1]);
  • NSDictionary的写入与读取
1
2
3
4
5
6
7
8
9
10
11
12
13
// 字典写入文件
// 创建一个存储字典的文件路径
NSString *fileDicPath = [docPath stringByAppendingPathComponent:@"love.txt"];
NSDictionary *dic = @{@"职业":@"程序员", @"梦想":@"代码无BUG"};
// 字典写入时执行的方法
[dic writeToFile:fileDicPath atomically:YES];
NSLog(@"fileDicPath is %@", fileDicPath);
// 从文件中读取数据字典的方法
NSDictionary *resultDic = [NSDictionary dictionaryWithContentsOfFile:fileDicPath];
NSLog(@"%@", resultDic[@"梦想"]);
  • NSData的写入与读取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// NSData写入文件
// 创建一个存放NSData数据的路径
NSString *fileDataPath = [docPath stringByAppendingPathComponent:@"icon"];
// 得到一个UIImage对象
UIImage *image = [UIImage imageNamed:@"icon.jpg"];
// 将UIImage对象转换成NSData对象
NSData *data = UIImageJPEGRepresentation(image, 0);
[data writeToFile:fileDataPath atomically:YES];
NSLog(@"fileDataPath is %@", fileDataPath);
// 从文件读取存储的NSData数据
NSData *resultData = [NSData dataWithContentsOfFile:fileDataPath];
// 将得到的NSData数据转换成原有的图片对象
UIImage *resultImage = [UIImage imageWithData:resultData];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
imageView.image = resultImage;
[self.view addSubview:imageView];

文件管理器与文件对接器

  • 文件管理器与文件连接器之间的区别
  • 文件管理器主要是对文件进行操作(创建、删除、改名等)以及文件信息的获取
  • 文件连接器主要是对文件内容进行读取与写入操作
  • 文件管理器
    使用文件管理器创建文件夹
1
2
3
4
5
6
7
8
9
10
11
12
13
// 获取到Caches文件夹路径
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
// 获取创建文件夹的路径
NSString *dirPath = [cachePath stringByAppendingPathComponent:@"testDirectroy"];
// 创建文件管理对象
NSFileManager *fileManager = [NSFileManager defaultManager];
// 创建文件夹
[fileManager createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:nil];
NSLog(@"%@", dirPath);
  • 通过文件管理器创建文件以及获取文件信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 得到Documents路径
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// 创建一个文件路径
NSString *filePath = [docPath stringByAppendingPathComponent:@"objc.txt"];
// 创建文件首先需要一个文件管理对象
NSFileManager *fileManager = [NSFileManager defaultManager];
// 创建文件
[fileManager createFileAtPath:filePath contents:[@"Objective-C" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
NSLog(@"%@",filePath);
// 获取某个文件或者某个文件夹的大小
NSDictionary *dic = [fileManager attributesOfItemAtPath:filePath error:nil];
NSLog(@"%@", dic);
NSNumber *number = [dic objectForKey:NSFileSize];
NSInteger size = number.intValue;
NSLog(@"%@", number);
  • 文件移动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建文件夹
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *pathPath = [docPath stringByAppendingPathComponent:@"path"];
// 创建文件
NSString *path = [pathPath stringByAppendingPathComponent:@"test.txt"];
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *dirPath = [cachePath stringByAppendingPathComponent:@"testDirectroy"];
NSString *desPath = [dirPath stringByAppendingPathComponent:@"test.txt"];
// 创建文件管理器对象,进行文件移动操作
NSFileManager *fileManager = [NSFileManager defaultManager];
// 移动文件
[fileManager moveItemAtPath:path toPath:desPath error:nil]; // 移动文件的核心代码
  • 文件对接器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 练习要求:从一个文件中指定的位置开始追加内容
提示:
1、在documents目录下创建一个test.txt文件,文件中的内容为"abcdefg"
2、从文件偏移量为3那个位置开始追加内容"1234"
*/
// 获取Documents文件夹路径
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// 创建文件路径
NSString *filePath = [docPath stringByAppendingPathComponent:@"test.txt"];
// 使用文件管理对象创建文件
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager createFileAtPath:filePath contents:[@"abcdefg" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
// 创建文件对接对象
NSFileHandle *handle = [NSFileHandle fileHandleForUpdatingAtPath:filePath]; // 文件对接对象此时针对文件既可以读取又可以写入
// 将偏移量挪到3的位置
[handle seekToFileOffset:3];
// 写入数据
[handle writeData:[@"1234" dataUsingEncoding:NSUTF8StringEncoding]];
// 执行完操作之后不要忘记关闭文件
[handle closeFile];

复杂对象的读写(I/O)操作

复杂对象指的是在Foundation框架内不存在的数据类,如自定以的Person类,像这种自定义的类是无法在程序内部通过writeToFile:这个方法写入到文件内
既然复杂对象无法使用内部方法进行数据持久化,那么只能通过将复杂对象转换成NSData,然后在通过上面的方法写入文件,而这种转换的步骤就被称为归档,从文件中读取NSData数据,将NSData转换为复杂对象,这个步骤就是反归档

  • 记住
  • 复杂对象写入文件的过程(复杂对象->归档->NSData->writeToFile)
  • 从文件中读取出复杂对象过程(读取文件->NSData->反归档->复杂对象)
  • 1.首先,复杂对象所属的类要遵守
  • 2.其次,实现协议中的两个方法:

    1
    2
    -(void)encodeWithCoder:(NSCoder *)aCoder; 序列化
    -(id)initWithCoder:(NSCoder *)aDecoder; 反序列化
  • 首先,遵守NSCoding协议

    1
    2
    3
    4
    5
    @interface Person:NSObject<NSCoding>
    @property(nonatomic,copy) NSString *name
    @property(nonatomic,assign) integer age;
    @end
  • 其次,实现协议中的两个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 对person对象进行归档时,此方法执行。
// 对person中想要进行归档的所有属性,进行序列化操作。
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
}
// 对person对象进行反归档时,该方法执行。
// 创建一个新的person对象,所有属性都是通过反序列化得到的。
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
// 准备一个NSMutableData, 用于保存归档后的对象
NSMutableData *data = [NSMutableData data];
// 创建归档工具
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingMutableData:data];
// 归档
[archiver encodeObject:p] forKey:@"p1"];
// 结束
[archiver finishEncoding];
// 拼音写入沙盒路径
NSString *caches = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
NSString *filePath = [caches stringByAppendingPathComPonent:@"person"];
// 写入沙盒
[data writeToFile:filePath atomically:YES];
// 反归档
// 从filePath文件路径读取
NSData *data = [NSData dataWithContentsOfFile:filePath];
// 反归档工具
NSKeyedUnarchiver *unArchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
// 反归档成对象
Person *p2 = [unArchiver decodeObjectForKey:@"p1"];
// 反归档结束
[unArchiver finshDeoding];