使用strtok解析mmaped文件?

时间:2015-11-24 21:30:05

标签: c string parsing mmap strtok

这是我的问题:我想映射文件" filename.txt",它基本上由每行两对字符串组成:

"string1 string2
 string3 string4
 string5 string6..."

然后我想用strtok分隔不同的字符串。

所以我像这样映射文件:

// open file
if ((fdsrc = open("filename.txt", O_RDONLY)) < 0) {
        fprintf(stderr, "src open error");
        exit(1);
    }

// get the size of the file
if (fstat(fdsrc, &statbuf) < 0) {
    fprintf(stderr, "fstat error");
    exit(1);
}

// mmap the file
if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdsrc, 0)) == (caddr_t) -1) {
    fprintf(stderr, "mmap src");
    exit(1);
}

当我运行

printf("src: %s \n", src);

它正确打印文件的内容!

但是当我试图分开单词

char* token;
token = strtok(src, " \n");
while (token != NULL) {
    token = strtok(NULL, " \n");
}

输出是Segmentation Fault。 为什么我不能使用StrTok呢?

3 个答案:

答案 0 :(得分:6)

strtok()修改其操作的字符串。假设您不想更改文件内容,则需要更改mmap()选项。

您正在以只读方式打开并映射文件:

if ((fdsrc = open("filename.txt", O_RDONLY)) < 0) {
...
if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdsrc, 0)) == (caddr_t) -1) {
...

使用PROT_READ|PROT_WRITEMAP_PRIVATE

映射文件
src = mmap(0, statbuf.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fdsrc, 0);
if (src == (caddr_t) -1) {

您可能需要使用O_RDWR而不是O_RDONLY

打开文件

请注意:

如果文件大小与用于映射的页面大小的多个完全匹配,则该文件将不是NUL终止的字符串,当strtok()尝试读取结束时,您可能会获得SIGSEGV映射。

在这种情况下,您可以在文件映射后紧跟mmap()零填充页面。

答案 1 :(得分:3)

您的文件只能与PROT_READ一起映射。但是strtok()会修改其第一个参数src,并获得分段错误。您需要在使用strtok之前制作可写副本,或者切换到仅读取其输入的机制。在我看来,将该缓冲区的保护更改为PROT_RW似乎很奇怪,特别是如果您打算在程序的其他位置使用该文件的未修改内容。

作为替代方案,我建议使用strstr()(或不需要nul-byte终止的替代实现)来定位行尾子字符串,然后开始下一次搜索找到最后一次出现,加上子串的长度。请参阅下面有关空字节终止的说明。一个简化的例子:

  const char *delim = "\n";                                                                                                                          
  const char *start = src;                                                                                                                           
  const char *end = NULL;                                                                                                                            
  const int srclen = statbuf.st_size;                                                                                                                
  const int delim_length = strlen(delim);                                                                                                            

  while (start && start < (src + srclen)) {                                                                                                          
    end = strstr(start, delim);                                                                                                                      

    if (NULL == end) {  
      // use of %.* to print at most X chars from string.                                                                                                                             
      printf("Token: %.*s\n", (int) (src + srclen - start), start);                                                                                 
      break;                                                                                                                                         
    } else {                                                                                                                                         
      printf("Token: %.*s\n", (int) (end - start), start);                                                                                           
      start = end + delim_length;                                                                                                                    
    }                                                                                                                                                
  }                     

mmap区域可能不会以空字节结尾(如评论所示)

strstr()适用于以null结尾的字符串。您的mmapped区域可能不以空字节结尾。内核可能会使用\0擦除最后一个mmapped内存页面的剩余部分(超过文件结尾)以避免进程之间的数据泄漏,但是如果您的文件长度恰好是页面的倍数-size,你在使用strstr()时会遇到麻烦 - 没有一个空字节可以让你回来。

您可以推出自己的小字符串查找器strnstr()。或者强制在末尾标记另一个空页。

答案 2 :(得分:2)

strtok()修改传递指针的char数组。

mmap该文件处于只读模式,因此当strtok尝试修改内存时,您会收到违规行为。

在读取+写入模式下mmap文件是个坏主意,该文件将被修改并可能已损坏。

strtok不适合您的目的,编写自己的匹配函数,不修改其参数数组并返回偏移量和长度。

另请注意,mmap ped内存不应超出文件大小而被访问,并且不一定'\0'终止,因此您不应使用字符串函数来搜索它({{ 1}},strchrstrstr ...)也不从中复制(strlen)。