- 16.3 基本的文件操作:NSFileHandle
- import<Foundation/NSObject.h>
- import<Foundation/NSString.h>
- import<Foundation/NSFileHandle.h>
- import<Foundation/NSFileManager.h>
- import<Foundation/NSAutoreleasePool.h>
- import<Foundation/NSData.h>
- import<Foundation/NSObject.h>
- import<Foundation/NSString.h>
- import<Foundation/NSFileHandle.h>
- import<Foundation/NSFileManager.h>
- import<Foundation/NSAutoreleasePool.h>
- import<Foundation/NSData.h>
16.3 基本的文件操作:NSFileHandle
利用NSFileHandle类提供的方法,允许更有效地使用文件。在本章的开始,我们列举了利用这些方法可以完成的一些操作。
一般而言,我们处理文件时都要经历以下三个步骤:
1.打开文件,并获取一个NSFileHandle对象,以便在后面的I/O操作中引用该文件。
2.对打开的文件执行I/O操作。
3.关闭文件。
表16-6总结了一些常用的NSFileHandle方法。在这个表中,fh是一个NSFileHandle对象,data是一个NSData对象,path是一个NSString对象,offset是一个unsigned long long。
上表中并未列出获取NSFileHandle以用于标准输入、标准输出、标准错误和空设备的方法。它们的格式为fileHandleWithDevice,其中Device可以是StandardInput、StandardOutput、StandardError或NullDevice。
这里没有列出用于后台(也就是,异步)读取和写入的方法。
应该注意到FileHandle类并没有提供创建文件的功能。前面描述过,必须使用FileManager方法来创建文件。因此,方法fileHandleForWritingAtPath:和fileHandleForUpdatingAtPath:都假定文件已存在,否则返回nil。对于这两个方法,文件的偏移量都设为文件的开始,所以都是在文件的开始位置开始写入(或更新模式的读取)。另外,如果在UNIX系统下编程应该注意,打开用于读取的文件,不要截断文件;如果想要这么做,不得不自己完成这项操作。
代码清单16-7打开本章开始创建的原始testfile文件,读取它的内容,并将其复制到名为testout的文件中。
代码清单16-7
//Some basic file handle operations
//Assumes the existence of a file called“testfile”
//in the current working directory
import<Foundation/NSObject.h>
import<Foundation/NSString.h>
import<Foundation/NSFileHandle.h>
import<Foundation/NSFileManager.h>
import<Foundation/NSAutoreleasePool.h>
import<Foundation/NSData.h>
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
NSFileHandleinFile,outFile;
NSData*buffer;
//Open the file testfile for reading
inFile=[NSFileHandle fileHandleForReadingAtPath:@“testfile”];
if(inFile==nil){
NSLog(@“Open of testfile for reading failed”);
return 1;
}
//Create the output file first if necessary
[[NSFileManager defaultManager]createFileAtPath:@“testout”
contents:nil attributes:nil];
//Now open outfile for writing
outFile=[NSFileHandle fileHandleForWritingAtPath:@“testout”];
if(outFile==nil){
NSLog(@“Open of testout for writing failed”);
return 2;
}
//Truncate the output file since it may contain data
[outFile truncateFileAtOffset:0];
//Read the data from inFile and write it to outFile
buffer=[inFile readDataToEndOfFile];
[outFile writeData:buffer];
//Close the two files
[inFile closeFile];
[outFile closeFile];
//Verify the files contents
NSLog(@,[NSString stringWithContentsOfFile:@“testout”encoding:
NSUTF8StringEncoding error:nil]);
[pool drain];
return 0;
}
代码清单16-7输出
This is a test file with some data in it.
Heres another line of data.
And a third..
方法readDataToEndOfFile:每次从文件中读取最多UINT_MAX个字节的数据,这个量定义在头文件<limits.h>中,并且在许多系统中值等于FFFFFFFF16。这个值对于你编写的任何应用程序而言,已经足够大了。还可以中断这项操作,以执行少量读取和写入。利用方法readDataOfLength:,甚至可以设置循环,一次在文件之间传输一缓冲区的字节。缓冲区的大小可能是8192(8Kb)字节,也可以是131072(128Kb)字节。经常使用的是2的乘方,这是因为底层的操作系统通常以块为单位执行I/O操作的,而块的大小一般为2的乘方个字节。可能要在系统上试用不同的值,以查看哪个值最适合。
如果读取方法到达文件的末尾,并且没有读到任何数据,那么这个方法将返回一个空的NSData对象(也就是,缓冲区中没有字节)。可以对这个缓冲区应用length方法,并测试长度是否等于零,以查看该文件中是否还剩有数据可以读取。
如果打开一个要更新的文件,则文件的偏移量要被设为文件的开始。通过在文件中定位(seeking),可以更改偏移量,然后执行该文件的读写操作。因此,要定位到文件(文件的句柄为databaseHandle)的第10个字节,可以编写如下消息表达式:
[databaseHandle seekToFileOffset:10];
通过获得当前的文件偏移量,然后加上或者减去这个值,就得到相对文件位置。因此,要跳过文件中当前位置之后的128个字节,编写如下代码:
[databaseHandle seekToFileOffet:
[databaseHandle offsetInFile]+128];
要在文件中向回移动5个整数所占的字节数,编写如下代码:
[databaseHandle seekToFileOffet:
[databaseHandle offsetInFile]-5*sizeof(int)];
代码清单16-8将一个文件的内容附加到另一个文件中。通过打开另一个用于写入的文件,然后定位到该文件的结尾,最后将第一个文件中的内容写入第二个文件中来实现。
代码清单16-8
//Append the file“fileA”to the end of“fileB”
import<Foundation/NSObject.h>
import<Foundation/NSString.h>
import<Foundation/NSFileHandle.h>
import<Foundation/NSFileManager.h>
import<Foundation/NSAutoreleasePool.h>
import<Foundation/NSData.h>
int main(int argc, char*argv[])
{
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc]init];
NSFileHandleinFile,outFile;
NSData*buffer;
//Open the file fileA for reading
inFile=[NSFileHandle fileHandleForReadingAtPath:@“fileA”];
if(inFile==nil){
NSLog(@“Open of fileA for reading failed”);
return 1;
}
//Open the file fileB for updating
outFile=[NSFileHandle fileHandleForWritingAtPath:@“fileB”];
if(outFile==nil){
NSLog(@“Open of fileB for writing failed”);
return 2;
}
//Seek to the end of outFile
[outFile seekToEndOfFile];
//Read inFile and write its contents to outFile
buffer=[inFile readDataToEndOfFile];
[outFile writeData:buffer];
//Close the two files
[inFile closeFile];
[outFile closeFile];
[pool drain];
return 0;
}
Contents of FileA before running Program 16.8
This is line 1 in the first file.
This is line 2 in the first file.
Contents of FileB before running Program 16.8
This is line 1 in the second file.
This is line 2 in the second file.
代码清单16-8输出
Contents of fileB
This is line 1 in the second file.
This is line 2 in the second file.
This is line 1 in the first file.
This is line 2 in the first file.
从输出可知,第一个文件的内容成功地附加到第二个文件的末尾。顺便说明一下,在搜索操作执行完毕之后,seekToEndOfFile返回当前文件的偏移量。选择忽略这个值;如果需要,可以使用这个信息来获得程序中文件的大小。