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。

16.3 基本的文件操作:NSFileHandle - 图1

上表中并未列出获取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返回当前文件的偏移量。选择忽略这个值;如果需要,可以使用这个信息来获得程序中文件的大小。