2014-11-24 45 views
3

当MMAP()荷兰国际集团的文本文件,就像这样如何解决从mmap()返回的字符串中缺少NUL终止符的问题?

int fd = open("file.txt", O_RDWR); 
fstat(fd, &sb) 
char *text = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 

文件内容被直接映射到存储器中,并text它将不包含一个NUL - 终止子与正常字符串函数在其上,以便操作不会安全。在Linux上(至少)未使用页面的剩余字节是零填充的,所以在文件大小不是页面大小倍数的所有情况下,您都可以得到NUL终止符。

但依靠感觉脏和其他mmap()实现(例如,在FreeBSD中,我认为)不会零填充部分页面。映射页面大小倍数的文件也将缺少NUL终止符。

是否有合理的方法来解决这个问题或添加NUL终结符?

事情我已经考虑

  1. 使用strn*()功能完全和跟踪距离的缓冲区的末尾。
    • 优点:无需NUL终止
    • 缺点:需要额外的追踪知道距离解析文本何时结束的文件;一些str*()功能没有strn*()对应,如strstr
  2. 由于建议使用another answer,请在文本文件映射后的固定地址进行匿名映射。
    • 优点:可以使用常规的C str*()功能
    • 缺点:使用MAP_FIXED不是线程安全的;看起来像一个可怕的黑客无论如何
  3. mmap()mmap()一个额外的字节,使地图可写,并写入NUL终止符。 OpenGroup的mmap man page表示,您可以制作比对象大小更大的映射,但访问实际映射对象之外的数据将生成SIGBUS
    • 优点:可以使用常规的C str*()功能
    • 缺点:(?忽略)需要处理SIGBUS,这可能意味着别的事情。我不确定编写NUL终结符会起作用吗?
  4. 将页面大小倍数为ftruncate()的文件扩展一个字节。
    • 优点:可以使用常规的C str*()功能; ftruncate()会为你新分配的区域写入NUL字节
    • 缺点:我们必须写入文件,这在所有情况下都是不可能或不可接受的;对于mmap()实现不填零
  5. 只是read()文件放入一些malloc()“,忘记了D内存部分页面不能解决问题有关mmap()
    • 优点:避免了所有的这些解决方案;为NUL易malloc()和额外的字节
    • 缺点:比不同的性能特征mmap()

解决方案#1通常似乎是最好的,只是需要在功能读取的部分一些额外的工作文本。

有更好的选择,还是这些是最好的解决方案?我没有考虑过这些解决方案的哪些方面会使它们更具吸引力?

+2

我的投票是#5。 [KISS](http://en.wikipedia.org/wiki/KISS_principle)。 – 2014-11-24 02:16:42

+0

想想#5。利弊。 mmap需要读取磁盘,所以读取。 Whay是一个骗局吗? BTW +1 @Johnathon Reinhart – 2014-11-24 02:40:10

+0

字符串详细信息:在C中,根据定义,字符串_always_具有终止''\ 0',否则它不是字符串。 'char'数组可能没有''\ 0''。除了命名之外,不会改变你的问题。典型的文本文件没有_any_字符串,但没有文本行。 – chux 2014-11-24 03:10:40

回答

2

我建议在这里进行范式转换。

您正在查看由定义文本的'\ 0'分隔字符串组成的整个Universe。为什么不用这种方式来看待世界,为什么不试着看看文本被定义为由开始和结束迭代器定义的序列的世界。

mmap文件,然后初步确定了开始迭代器,把它beg_iter到MMAP-ED段的开始,结束迭代器,调用它end_iter,第一个字节的mmap-的最后一个字节以下ED段或beg_iter+number_of_pages*pagesize,然后直到

A)end_iter等于beg_iter,或

B)beg_iter[-1]不是一个空字符,则

C)递减end_iter,并返回步骤A.

完成后,您将有一对迭代器,开始迭代器值和定义您的文本字符串的结束迭代器值。

当然,在这种情况下,你的迭代器是普通的char *,但这真的不是很重要。重要的是,现在您发现自己拥有一套来自C++标准库的丰富的算法和模板,可以让您实施许多复杂的操作,既可变(如std::transform),也可以不可变(如std::find) 。

以空格结尾的字符串实际上是从plain C时代起的一种保留。对于C++,空字符结尾的字符串有些过时,并且很平常。现代C++代码应该使用std::string对象,并使用开始和结束迭代器定义的序列。一个小的脚注:你可能会发现fstat()文件变得更加容易,并且获得文件的精确长度(以字节为单位),而不是mmap-它。那么你现在完全知道得到了很多mmaped,并且你不必通过查看填充来反向工程。

+0

感谢您的回答。我真的想用C'str *()'函数寻找一种可用于C语言的解决方案,但基本上它听起来像你所建议的类似于解决方案#1。关于'fstat()':绝对 - 我在我的例子中使用它。 – mattst88 2014-11-24 04:17:43

+0

关于你的答案的更多思考,我认为你绝对是在存储一个指向文本结尾的指针。这可以让你简单地计算你有多少剩余的减法。有一个upvote! – mattst88 2014-11-24 06:47:42