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

 

 

posted on 2018-10-13 01:18  梁飞宇  阅读(2041)  评论(0)    收藏  举报