如何在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个额外的八位字节来读取字符边界是相当简单的。
一种方法是
- 读到某一点 –
- 然后检查最后一个字节以确定它是否正在分割一个UTF-8字符
- 如果不是 – 阅读下一个块
- 如果是的话,得到下一个字节并修复 – 然后读取下一个块
utf8是自我同步的 – 只是根据需要多读一些,然后读取字节值来确定任何代码点的边界。
另外,你可以使用fopen
并在堆栈上使用一个小的,易于pipe理的缓冲区,而内存不会成为问题。