iOS之文件管理(四)—NSFileHandle
一,概述
NSFileHandle类允许更有效的使用文件,可以实现如下功能:
1、打开一个文件,执行读、写或更新读写操作;
2、在文件中查找指定位置;
3、从文件中读取特定数目的字节,或将特定数目的字节写入文件中
另外,NSFileHandle类提供的方法也可以用于各种设备或套接字。一般而言,我们处理文件时都要经历三个步骤:打开文件,获取一个NSFileHandle对象;对打开文件执行相关操作;关闭文件。
备注:
NSFileHandle 此类主要是对文件内容进行读取和写入操作
NSFileMange 此类主要是对文件进行的操作以及文件信息的获取
二,常用处理方法
+ (id)fileHandleForReadingAtPath:(NSString *)path 打开一个文件准备读取 + (id)fileHandleForWritingAtPath:(NSString *)path 打开一个文件准备写入 + (id)fileHandleForUpdatingAtPath:(NSString *)path 打开一个文件准备更新 - (NSData *)availableData; 从设备或通道返回可用的数据 - (NSData *)readDataToEndOfFile; 从当前的节点读取到文件的末尾 - (NSData *)readDataOfLength:(NSUInteger)length; 从当前节点开始读取指定的长度数据 - (void)writeData:(NSData *)data; 写入数据 - (unsigned long long)offsetInFile; 获取当前文件的偏移量 - (void)seekToFileOffset:(unsigned long long)offset; 跳到指定文件的偏移量 - (unsigned long long)seekToEndOfFile; 跳到文件末尾 - (void)truncateFileAtOffset:(unsigned long long)offset; 将文件的长度设为offset字节 - (void)closeFile; 关闭文件
向文件追加数据
NSString *homePath = NSHomeDirectory( ); NSString *sourcePath = [homePath stringByAppendingPathConmpone:@"testfile.text"]; NSFileHandle *fielHandle = [NSFileHandle fileHandleForUpdatingAtPath:sourcePath]; [fileHandle seekToEndOfFile]; 将节点跳到文件的末尾 NSString *str = @"追加的数据" NSData* stringData = [str dataUsingEncoding:NSUTF8StringEncoding]; [fileHandle writeData:stringData]; 追加写入数据 [fileHandle closeFile];
定位数据
NSFileManager *fm = [NSFileManager defaultManager]; NSString *content = @"abcdef"; [fm createFileAtPath:path contents:[content dataUsingEncoding:NSUTF8StringEncoding] attributes:nil]; NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path]; NSUInteger length = [fileHandle availabelData] length]; 获取数据长度 [fileHandle seekToFileOffset;length/2]; 偏移量文件的一半 NSData *data = [fileHandle readDataToEndOfFile]; [fileHandle closeFile];
复制文件
NSFileHandle *infile, *outfile; 输入文件、输出文件 NSData *buffer; 读取的缓冲数据 NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *homePath = NSHomeDirectory( ); NSString *sourcePath = [homePath stringByAppendingPathComponent:@"testfile.txt"]; 源文件路径 NSString *outPath = [homePath stringByAppendingPathComponent:@"outfile.txt"]; 输出文件路径 BOOL sucess = [fileManager createFileAtPath:outPath contents:nil attributes:nil]; if (!success){ return N0; } infile = [NSFileHandle fileHandleForReadingAtPath:sourcePath]; 创建读取源路径文件 if (infile == nil){ return NO; } outfile = [NSFileHandle fileHandleForReadingAtPath:outPath]; 创建病打开要输出的文件 if (outfile == nil){ return NO; }
[outfile truncateFileAtOffset:0]; 将输出文件的长度设为0 buffer = [infile readDataToEndOfFile]; 读取数据 [outfile writeData:buffer]; 写入输入 [infile closeFile]; 关闭写入、输入文件 [outfile closeFile];
三,示例
二、NSFileHandle类的方法介绍和使用,并加举例。 #import "ViewController.h" #import <sys/socket.h> #import <netinet/in.h> #import <arpa/inet.h>
@implementation ViewController (KNSFileHandle) -(void)viewDidLoad{ /** 首先创建一个文件 */ [self createFile]; } -(void)createFile{ if (!filePath) { filePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; filePath =[NSString stringWithFormat:@"%@/test.txt",filePath]; } /** 生成文件的属性设置 */ NSDictionary * attributes = @{NSFileOwnerAccountName:@"NetWork小贱"};
/** 创建写入的数据 */ NSData * saveData = [@"成功QQ吧" dataUsingEncoding:NSUTF8StringEncoding] ; NSFileManager * fileManager = [NSFileManager defaultManager]; BOOL isFile = [fileManager createFileAtPath:filePath contents:saveData attributes:attributes]; if (!isFile) { printf("创建失败"); return; }
/** 读 */ // [self createReadingFileHandle]; /** 写 */ // [self createWritingFileHandle]; /** 更新数据 */ // [self createUpdatingFileHandle]; /** 文件的通知 */ // [self notifitionFileHandle]; /** 两个回调 read 和 write */ // [self fileHandleBlock]; /** 服务通知——socket */ [self socketFileHandle]; } #pragma mark --- create createReadingFileHandle -(void)createReadingFileHandle{ /** 三种创建方法 另外,下面的这个不常用: - (instancetype)initWithFileDescriptor:(int)fd closeOnDealloc:(BOOL)closeopt NS_DESIGNATED_INITIALIZER; */ // 打开文件准备读取 NSFileHandle * fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath]; /** 按长度读取数据 */ [self analysis:[fileHandle readDataOfLength:3]]; /** 读取文件内容 */ [self analysis:[fileHandle readDataToEndOfFile]]; /** 可获取的数据 */ [self analysis:[fileHandle availableData]]; /** 获取文件的操作位置
例如: 文件内有:成功QQ吧 ,然后再使用 [self analysis:[fileHandle readDataOfLength:3]] 之后,再调用 [fileHandle offsetInFile] 就可得到 3。因为,一个汉字占用3个字节,我们开始是读取3个字节,所以现在的文件操作位置是 3. */ NSInteger length = [fileHandle offsetInFile]; NSLog(@"%ld",length); /** 设置当前文件的操作位置 @param 输出: 2017-03-24 13:15:36.912 KNSFileHandle[3469:749724] 11 2017-03-24 13:15:36.912 KNSFileHandle[3469:749724] 3 */ [fileHandle seekToFileOffset:3]; NSInteger lengthSet = [fileHandle offsetInFile]; NSLog(@"%ld",lengthSet); /** seekToEndOfFile 是设置文件操作位置到文件的末尾 输出: 2017-03-24 13:17:45.497 KNSFileHandle[3505:759909] 3 2017-03-24 13:17:45.497 KNSFileHandle[3505:759909] 11 */ [fileHandle seekToEndOfFile]; NSInteger lengthSetLast = [fileHandle offsetInFile]; NSLog(@"%ld",lengthSetLast);
/** 同步文件 */ [fileHandle synchronizeFile]; /** 关闭文件 */ [fileHandle closeFile]; /*
// 测试读取创建的是否可以写入 [fileHandle writeData:[self createData:@"特别推出"]]; // 我们在读取 [self analysis:[fileHandle readDataToEndOfFile]]; */ /**** 总结 1、读取文件创建的NSFileHandle对象,不可用于写入NSFileHandle对象 2、NSFileHandle 读取文件是从文件中取出数据,被取出的数据在文件中被删除 3、读取创建的 NSFileHandle对象 ,不可使用 truncateFileAtOffset 设置文件大小 ********/ } #pragma mark --- createWritingFileHandle -(void)createWritingFileHandle{ /** 打开并写入 */ NSFileHandle * fileHandle = [NSFileHandle fileHandleForWritingAtPath:filePath]; /** 我们测试看能否读取 [self analysis:[fileHandle availableData]]; */ /** 设置写入文件的大小 */ [fileHandle truncateFileAtOffset:14]; /** 写入数据 */ [fileHandle writeData:[self createData:@"特别提供"]]; /** 将文件的操作设置到最后 */ [fileHandle seekToEndOfFile]; NSInteger lengthSet = [fileHandle offsetInFile]; NSLog(@"%ld",lengthSet); /*** 输出:2017-03-24 13:41:03.731 KNSFileHandle[3851:859436] 22
问题:为什么输出的是 22 不是 23 ??? 答: 因为写入文件前的文件操作位置是 11,我们又将操作位置设置为 10,然后,我们是从10的操作位置到12,则,最后的操作位置是 22,不是23 。
**/ /** 写入完要同步一下 */ [fileHandle synchronizeFile]; [fileHandle closeFile]; /** 我们查看最后的结果 输出: 2017-03-24 13:55:38.997 KNSFileHandle[4187:912750] Result:成功QQ吧 */ NSFileHandle * fileHandleRead = [NSFileHandle fileHandleForReadingAtPath:filePath]; [self analysis:[fileHandleRead readDataToEndOfFile]]; [fileHandleRead closeFile]; /**** 总结 1、写入文件创建的NSFileHandle对象,不可用与读取数据 2、由fileHandleForWritingAtPath 创建的 NSFileHandle对象 ,在写入数据是覆盖不是增加 3、注意,如果在文件写入前设置 truncateFileAtOffset ; 如果设置的数字小于要写入的文件长度,则后期再获取文件的内容为空 ********/ } #pragma mark --- createUpdatingFileHandle -(void)createUpdatingFileHandle{ /** 打开并更新 */ NSFileHandle * fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath]; /** 测试更新状态是否可以读取数据
输出: 2017-03-24 14:10:01.971 KNSFileHandle[4326:965690] Result:成功QQ吧 */ [self analysis:[fileHandle availableData]]; /** 跳转操作节点 */ [fileHandle seekToEndOfFile]; /** 测试更新状态是否可以写入 */ [fileHandle writeData:[self createData:@"特别提供"]]; [fileHandle synchronizeFile]; [fileHandle closeFile]; /** 我们读取数据 */ NSFileHandle * fileHandleWrite = [NSFileHandle fileHandleForUpdatingAtPath:filePath]; [self analysis:[fileHandleWrite availableData]]; /*** 注意: 使用fileHandleForUpdatingAtPath 创建的对象A 打开更新文件。不能写入后,再使用A,来获取写入的内容 ****/ } #pragma mark --- notifitionFileHandle -(void)notifitionFileHandle{ /** 文件数据读取到最后的时候发出通知 */ NSFileHandle * fileHandleReadToEnd = [NSFileHandle fileHandleForReadingAtPath:filePath]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(ReadToEndNotification:) name:NSFileHandleReadToEndOfFileCompletionNotification object:fileHandleReadToEnd]; [fileHandleReadToEnd readToEndOfFileInBackgroundAndNotify]; [self analysis:[fileHandleReadToEnd readDataToEndOfFile]]; /** 文件读取中,包括文件开始被读取,都发出通知 */ NSFileHandle * fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(readCompletionNotification:) name:NSFileHandleReadCompletionNotification object:fileHandle]; [fileHandle readInBackgroundAndNotify]; [self analysis:[fileHandle availableData]]; /** 输出: 2017-03-24 18:33:10.110 KNSFileHandle[7056:1676167] 文件被读取了 2017-03-24 18:33:10.111 KNSFileHandle[7056:1676167] 文件被读取到最后了 注意: 一个是在文件开始和读取中发送通知, ; 另一个是在文件读取完成发送通知 **/ /* socket建立。对于每个监听到的连接通知,我们可以从fileDescriptor 构造一个NSFileHandle 以接受请求。就会触发 NSFileHandleConnectionAcceptedNotification 通知。 NSFileHandle* listeningHandle = [[NSFileHandle alloc]initWithFileDescriptor:fileDescriptor closeOnDealloc:YES]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectionAcceptedNotification:) name:NSFileHandleConnectionAcceptedNotification object:listeningHandle]; [listeningHandle acceptConnectionInBackgroundAndNotify]; [self analysis:[listeningHandle availableData]]; */ /** 这个是当文件内部检测到可用数据的时候,会发起通知 */ NSFileHandle * fileHandleDataAvailable = [NSFileHandle fileHandleForReadingAtPath:filePath]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataAvailableNotification:) name:NSFileHandleDataAvailableNotification object:fileHandleDataAvailable]; [fileHandleDataAvailable waitForDataInBackgroundAndNotify]; [self analysis:[fileHandleDataAvailable availableData]]; } #pragma mark --- readCompletionNotification -(void)readCompletionNotification:(NSNotification*)notification{ NSLog(@"文件被读取了"); } #pragma mark --- ReadToEndNotification -(void)ReadToEndNotification:(NSNotification*)notification{ NSLog(@"文件被读取到最后了"); } #pragma mark --- ReadToEndNotification -(void)connectionAcceptedNotification:(NSNotification*)notification{ NSLog(@"connectionAcceptedNotification"); } #pragma mark --- ReadToEndNotification -(void)dataAvailableNotification:(NSNotification*)notification{ NSLog(@"dataAvailableNotification,文件内部检测到可用数据"); } #pragma mark --- fileHandleBlock -(void)fileHandleBlock{ /** 写 */ NSFileHandle * fileHandleWriteability = [NSFileHandle fileHandleForWritingAtPath:filePath]; fileHandleWriteability.writeabilityHandler = ^(NSFileHandle* handle){ /** 注意: 此处的 handle 不可用于写入 */ [handle writeData:[self createData:@"特别提供"]]; [handle synchronizeFile]; [handle closeFile]; /** 获取数据 */ [self analysis:[NSData dataWithContentsOfFile:filePath]]; }; [[NSPipe pipe].fileHandleForWriting writeabilityHandler]; /** 读 */ NSFileHandle * fileHandleReadability = [NSFileHandle fileHandleForReadingAtPath:filePath]; fileHandleReadability.readabilityHandler = ^(NSFileHandle* handle){ [self analysis:[handle readDataToEndOfFile]]; }; [[NSPipe pipe].fileHandleForReading readabilityHandler]; /** 注意: 发起调用,可以是任意对象的 NSFileHandle 对象。 创建的对象个是个的用途,不可用于其他用途,比如:读的对象就是读,不可用于写 */ } #pragma mark --- socketFileHandle -(void)socketFileHandle{ /** 创建一个socket 知识扩展: TCP端口12345使用传输控制协议。TCP是TCP/IP网络中的主要协议之一。TCP是面向连接的协议,它要求握手建立端到端的通信。只有在连接建立时,用户的数据才能在连接上双向发送。 注意!TCP保证在端口12345上传送数据包的顺序与它们发送的顺序相同。TCP端口12345的保证通信是TCP和UDP的主要区别。UDP端口12345不会保证TCP的通信。 UDP端口12345提供不可靠的服务和数据报可能会重复、无序,或不通知。在端口12345上的UDP认为在应用程序中不需要进行错误检查和校正,从而避免了在网络接口级别上进行此类处理的开销。 UDP(用户数据报协议)是一个面向消息的最小传输层协议(协议在IETF RFC 768中被记录)。 应用实例,经常使用UDP:语音IP(VoIP),流媒体和实时多人游戏。许多Web应用程序使用UDP,如域名系统(DNS),路由信息协议(RIP),动态主机配置协议(DHCP),简单的网络管理协议(SNMP)。 TCP与UDP TCP:可靠的,有序的,重量级的,UDP流;不可靠的,无序的,轻量级的,报。 */ NSSocketPort * socketPort = [[NSSocketPort alloc]initWithTCPPort:12345]; NSSocketNativeHandle socketId = [socketPort socket]; /** 创建一个 NSFilehandle 对象 */ NSFileHandle * socketFileHandle = [[NSFileHandle alloc]initWithFileDescriptor:socketId]; /** 添加链接服务器监控 */ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(socketConnectionAcceptedNotification:) name:NSFileHandleConnectionAcceptedNotification object:socketFileHandle]; [socketFileHandle acceptConnectionInBackgroundAndNotify]; /** 连接服务器,并发送消息 */ [self connectionServer]; } #pragma mark --- connectionAcceptedNotification -(void)socketConnectionAcceptedNotification:(NSNotification*)notification{ /** 获取有请求服务器创建的 NSFileHandle 对象 */ NSFileHandle * connectedSocketFileHandle = [[notification userInfo] objectForKey:NSFileHandleNotificationFileHandleItem]; /** 在监控数据的读取 */ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(readFileHandle:) name:NSFileHandleReadCompletionNotification object:connectedSocketFileHandle]; /** 发送消息给客户端,确认连接已被接受 */ [connectedSocketFileHandle writeData:[self createData:@"已经连接成功"]]; /** 开启监控 */ [connectedSocketFileHandle readInBackgroundAndNotify]; } #pragma mark --- readFileHandle -(void)readFileHandle:(NSNotification*)notification{ NSData * data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem]; /** 对数据的解析 */ [self analysis:data]; /** 告诉文件句柄继续等待数据 */ [[notification object] readInBackgroundAndNotify]; } -(void)connectionServer{ /** 创建socket对象 @param kCFAllocatorDefault 内存分配的类型 @param PF_INET 协议族,一般为Ipv4:PF_INET,(Ipv6,PF_INET6) @param SOCK_STREAM 套接字类型,TCP用流式—>SOCK_STREAM,UDP用报文式->SOCK_DGRAM @param IPPROTO_TCP 指定通信协议.如果前一个参数为SOCK_STREAM,则默认使用TCP协议;如果前一个参数SOCK_DGRAM,则默认使用UDP协议 @param kCFSocketNoCallBack 回调的函数类型,有好多类型 @param callout 回调的第2中类型 @param context 用户定义的数据指针,用于对CFSocket对象的额外定义或者申明,可以为NULL */ CFSocketRef socketRef = CFSocketCreate(kCFAllocatorDefault,PF_INET,SOCK_STREAM,IPPROTO_TCP,kCFSocketNoCallBack,nil,NULL); if (socketRef!=nil) { /** 创建 socket 对象的地址,网络类型 */ struct sockaddr_in addR4; memset(&addR4, 0, sizeof(addR4)); addR4.sin_len = sizeof(addR4); addR4.sin_family = AF_INET; /** 设置连接服务器的地址 */ addR4.sin_addr.s_addr = inet_addr("127.0.0.1"); /** 设置远程监听的端口 */ addR4.sin_port = htons(12345); /** 进行地址转换 */ CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (UInt8*)&addR4, sizeof(addR4)); /** 连接服务器,并返回结果 */ CFSocketError socketError = CFSocketConnectToAddress(socketRef, dataRef, 10); if (socketError == kCFSocketSuccess) { NSLog(@"连接服务器成功"); const char * stringToSend = [@"NetWork小贱" UTF8String]; send(CFSocketGetNative(socketRef),stringToSend, strlen(stringToSend)+1, 1); /** 输出: 2017-03-27 17:39:05.921 KNSFileHandle[4453:1317371] 连接服务器成功 2017-03-27 17:39:08.647 KNSFileHandle[4453:1317371] Result:NetWork小贱 */ } } } #pragma mark --- createData 生成 -(NSData*)createData:(NSString*)string{ NSData * saveData = [string dataUsingEncoding:NSUTF8StringEncoding] ; return saveData; } #pragma mark --- analysis 解析 -(void)analysis:(NSData*)data{ NSString * string = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"\nResult:%@",string); } /*** 输出的结果: 1、2017-03-24 11:34:59.606 KNSFileHandle[2436:574179] 成功QQ吧 2、2017-03-24 11:53:27.531 KNSFileHandle[2792:653731] Result:成 2017-03-24 11:53:27.532 KNSFileHandle[2792:653731] Result:功QQ吧 */ @end