C:解析文件以获取mmap读取时的列数

时间:2017-05-19 14:19:58

标签: c parsing mmap strtok

我有一个如下文件:

1-3-5  2       1  
2      3-4-1   2
4-1    2-41-2  3-4  

我想返回此文件的列数。我正在用C中的mmap读取文件。我一直在尝试使用strtok(),但到目前为止失败了。这只是一个测试文件,我的原始文件是GB规模。

pmap = mmap(0,mystat.st_size,PROT_READ|PROT_WRITE,MAP_PRIVATE,fd,0);
char *start = pmap; 
char *token; 
token = strtok(start, "\t"); 
while (token != NULL){
     printf("%s \n",token);
     token = strtok(NULL, "\t");
     col_len++; 
    }

我一直在尝试这些方面的东西,但是,显然存在逻辑错误。我得到以下输出:

number of cols = 1   

虽然,列数应为3.
如果你们可以帮助解决如何使用mmap解析这种文件的问题,那就太好了。 我正在使用mmap,因为对文件的单次传递执行速度更快。

2 个答案:

答案 0 :(得分:3)

如果没有明确的问题,很难提供明确的答案;如上所述,该问题不包含完整代码,不显示精确输入,也不显示调试输出。

但是有可能根据strtok对此问题的不适用性提供一些建议。

strtok修改了它的第一个参数,因此将它与mmap ed资源一起使用确实不是一个好主意。但是,这与您遇到的问题没有直接关系。)

  1. 您应确保文件中的列实际上由制表符分隔。在我看来,文件最有可能包含空格,而不是制表符,这就是程序报告整个文件包含一列的原因。如果这是唯一的问题,您可以使用第二个参数strtok而不是" \t"来调用"\t"。但请记住,strtok将连续的分隔符组合到一个分隔符中,因此如果文件是以制表符分隔的并且有空字段,strtok将不会报告空字段。

  2. 与短语"整个文件相关"上面,您不会告诉strtok将换行符识别为终止令牌。因此strtok循环将尝试分析整个文件,将每行的最后一个字段计为与下一行的第一个字段相同的标记的一部分。那肯定不是你想要的。

    但是,strtok会覆盖它找到的列分隔符,因此如果您确实修复了strtok调用以包含\n作为分隔符字符,那么您将无法再分辨线条结束的地方。这可能对您的代码很重要,这也是strtok在这种情况下不适合的工具的关键原因。 Gnu strtok联机帮助页(man strtok,强调添加)提供了有关此问题的警告(最后在BUGS部分):

      

    使用这些功能时要小心。如果您确实使用它们,请注意:

         
        
    • 这些函数修改了他们的第一个参数。

    •   
    • 这些函数不能用于常量字符串。

    •   
    • 分隔字节的标识已丢失。

    •   
  3. 无法保证文件以NUL字符结尾。事实上,该文件不太可能包含NUL字符,并且在mmap的ed区域中引用不在文件中的字节是未定义的行为,但实际上大多数操作系统将映射整数页面,零填充最后一页。因此4096中有4095次,你不会注意到这个问题,而第4096次文件恰好是一个整数页面,你的程序会崩溃和烧毁,以及它控制的任何敏感设备。这是strtok永远不应在mmap个ed文件上使用的另一个原因。

答案 1 :(得分:1)

我的评论实际上不正确,因为您使用MAP_PRIVATE,您不会冒破坏文件的风险。但是,如果您修改了内存区域,则会复制触摸的页面,您可能不需要这些开销,否则您可以从一开始就将文件复制到RAM中。所以我仍然说:不要在这里使用strtok()

基于<ctype.h>中的函数,具有自己的循环的解决方案非常简单。正如我想亲自尝试一样,请在此处查看演示它的工作程序(相关部分是countCols()函数):

#define _POSIX_C_SOURCE 200112L               
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int countCols(const char *line, size_t maxlen)
{
    int cols = 0;
    int incol = 0;
    const char *c = line;

    while (maxlen && (!isspace(*c) || isblank(*c)))
    {
        if (isblank(*c))
        {
            incol = 0;
        }
        else
        {
            if (!incol)
            {
                incol = 1;
                ++cols;
            }
        }
        ++c;
        --maxlen;
    }

    return cols;
}

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s [file]\n", argv[0]);
        return EXIT_FAILURE;
    }

    struct stat st;
    if (stat(argv[1], &st) < 0)
    {
        fprintf(stderr, "Could not stat `%s': %s\n", argv[1],
                strerror(errno));
        return EXIT_FAILURE;
    }

    int dataFd = open(argv[1], O_RDONLY);
    if (dataFd < 0)
    {
        fprintf(stderr, "Could not open `%s': %s\n", argv[1],
                strerror(errno));
        return EXIT_FAILURE;
    }

    char *data = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, dataFd, 0);
    if (data == MAP_FAILED)
    {
        close(dataFd);
        fprintf(stderr, "Could not mmap `%s': %s\n", argv[1],
                strerror(errno));
        return EXIT_FAILURE;
    }

    int cols = countCols(data, st.st_size);

    printf("found %d columns.\n", cols);

    munmap(data, st.st_size);

    return EXIT_SUCCESS;
}