如何在iPhone上读取大型的UTF-8文件?

我的应用下载了一个UTF-8格式的文件,这个文件太大而无法使用NSString initWithContentsOfFile方法读取。 我NSFileHandle readDataOfLength的问题是, NSFileHandle readDataOfLength方法读取指定数量的字节,我可能最终只读取UTF-8字符的一部分。 这里最好的解决scheme是什么?

后来:

在船上的日志中logging下面的代码:

  NSData *buf = [NSData dataWithContentsOfFile:path options:NSDataReadingMappedIfSafe error:nil]; NSString *data = [[[NSString alloc] initWithBytesNoCopy:(void *)buf.bytes length:buf.length encoding:NSUTF8StringEncoding freeWhenDone:NO] autorelease]; 

我的主要问题实际上是与编码有关,而不是阅读文件的任务。

您可以使用NSData +dataWithContentsOfFile:options:error:NSDataReadingMappedIfSafe选项将您的文件映射到内存而不是加载它。 所以这将使用iOS中的虚拟内存pipe理器,以确保文件的位被交换进内存,就像桌面操作系统处理它的磁盘虚拟内存文件一样。 所以你不需要足够的RAM来将整个文件一次保存在内存中,只需要文件足够小,以适应处理器的地址空间(所以,千兆字节)。 你会得到一个对象,就像一个正常的NSData ,这应该节省大部分与使用NSFileHandle和手动stream式相关的麻烦。

您可能需要将部分转换为NSString因为您可以切实地期望从UTF-8转换为另一种格式(尽pipe它可能不是;值得一试-initWithData:encoding:看看NSString是否够智能只是为了保留对原始数据的引用,并根据需要从UTF-8扩展),我认为这是您的问题真正得到的。

我build议你使用-initWithBytes:length:encoding:将合理数量的字节转换为string。 然后你可以使用-lengthOfBytesUsingEncoding:来找出它实际上有多less字节的意义,并适当地提高你的读指针。 这是一个安全的假设, NSString将在您提供的字节末尾丢弃任何部分字符。

编辑:所以,像这样的东西:

 // map the file, rather than loading it NSData *data = [NSData dataWithContentsOfFile:...whatever... options:NSDataReadingMappedIfSafe error:&youdDoSomethingSafeHere]; // we'll maintain a read pointer to our current location in the data NSUinteger readPointer = 0; // continue while data remains while(readPointer < [data length]) { // work out how many bytes are remaining NSUInteger distanceToEndOfData = [data length] - readPointer; // grab at most 16kb of them, being careful not to read too many NSString *newPortion = [[NSString alloc] initWithBytes:(uint8_t *)[data bytes] + readPointer length:distanceToEndOfData > 16384 ? 16384 : distanceToEndOfData encoding:NSUTF8StringEncoding]; // do whatever we want with the string [self doSomethingWithFragment:newPortion]; // advance our read pointer by the number of bytes actually read, and // clean up readPointer += [newPortion lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; [newPortion release]; } 

当然,一个隐含的假设是,所有的UTF-8编码都是独一无二的,我不得不承认,绝对不能确定地说。

实际上很容易判断你是否用UTF-8分割了一个多字节字符。 连续字符都有这样设置的两个最重要的位: 10xxxxxx 。 所以如果缓冲区的最后八位字节有这样的模式,向后扫描find一个没有这种forms的八位字节。 这是angular色的第一个八位字节。 在八位字节中最重要的0的位置告诉你字符中有多less个八位字节

 0xxxxxxx => 1 octet (ASCII) 110xxxxx => 2 octets 1110xxxx => 3 octets 

等等,最多6个八位字节。

所以找出多less个额外的八位字节来读取字符边界是相当简单的。

一种方法是

  1. 读到某一点 –
  2. 然后检查最后一个字节以确定它是否正在分割一个UTF-8字符
  3. 如果不是 – 阅读下一个块
  4. 如果是的话,得到下一个字节并修复 – 然后读取下一个块

utf8是自我同步的 – 只是根据需要多读一些,然后读取字节值来确定任何代码点的边界。

另外,你可以使用fopen并在堆栈上使用一个小的,易于pipe理的缓冲区,而内存不会成为问题。