【问题标题】:Reading a specific number of lines from a file in C (scanf, fseek,fgets)从 C 中的文件中读取特定行数(scanf、fseek、fgets)
【发布时间】:2010-10-13 11:28:31
【问题描述】:

我有一个进程主进程,它产生 N 个子进程,它们通过未命名的管道与父进程通信。我必须能够:

  • 让父亲打开文件,然后向每个孩子发送一个结构,告诉它必须从 minma​​x 行读取;
  • 这会同时发生,所以我不知道:
  • 第一个如何划分N个地图的total_lines和
  • 第二个如何让每个孩子只读它应该读的行?

我的问题与操作系统无关。概念,只有文件操作:S

也许是 fseek?我无法映射日志文件(有些超过 1GB)。

我会很感激一些想法。提前谢谢你

编辑:我试图让孩子们在不使用 fseek 和块的值的情况下阅读相应的行,所以,有人可以告诉我这是否有效吗? :

//somewhere in the parent process:

    FILE* logFile = fopen(filename, "r");
                while (fgets(line, 1024, logFile) != NULL) {
                    num_lines++;
                }
                rewind(logFile);
                int prev = 0;
                for (i = 0; i < maps_nr; i++) {
                    struct send_to_Map request;
                    request.fp = logFile;
                    request.lower = lowLimit;
                    request.upper = highLimit;

                    if (i == 0)
                        request.minLine = 0;
                    else
                        request.minLine = 1 + prev;
                    if(i!=maps_nr-1)
                        request.maxLine = (request.minLine + num_lines / maps_nr) - 1;
                    else
                       request.maxLine = (request.minLine + num_lines / maps_nr)+(num_lines%maps_nr);
                    prev = request.maxLine;

                }
                //write this structure to respective pipe


//child process:

while(1) {
      ...
      //reads the structure to pipe (and knows which lines to read)
      int n=0, counter=0;
      while (fgets(line, 1024, logFile) != NULL){
         if (n>=minLine and n<=maxLine)
              counter+= process(Line);//returns 1 if IP was found, in that line, between the low and high limit
         n++; 
      }
     //(...) 
}

我不知道它是否会起作用,我只是让它起作用!即使这样,是否有可能优于单个进程读取整个文件并打印在日志文件中找到的 ips 总数?

【问题讨论】:

  • 我不知道有什么方法可以在不读取整个文件的情况下计算文件中的行数(您的 total_lines 变量)。这可以接受吗?
  • 不要让孩子阅读那些不是瓶颈无法改善的文件。让主人读取文件,然后将这些行发送给孩子。

标签: c file parent-child scanf fseek


【解决方案1】:

如果您不关心精确均匀地划分文件,并且行长度的分布在整个文件中有些均匀,则可以避免在父级中读取整个文件一次。

  1. 获取文件大小。
  2. chunk_size = file_size / number_of_children
  3. 当您生成每个子代时,请在父代中执行:
    • 寻求 (child_num+1) * chunk_size
    • 向前阅读,直到找到换行符。
    • 生成子块,告诉它从前一个块的末尾开始(或第一个子块为 0),以及块的实际长度。
  4. 每个孩子寻找start 并读取chunk_size 字节。

这是策略的粗略草图。

已编辑以简化一些事情。

编辑:下面是第 3 步和第 4 步的一些未经测试的代码。这一切都未经测试,我也没有注意过一个错误,但它让您了解fseekftell 的用法,这听起来像您正在寻找的东西。

// Assume FILE* f is open to the file, chunk_size is the average expected size,
// child_num is the id of the current child, spawn_child() is a function that
// handles the logic of spawning a child and telling it where to start reading,
// and how much to read. child_chunks[] is an array of structs to keep track of
// where the chunks start and how big they are.
if(fseek(f, child_num * chunk_size, SEEK_SET) < 0) { handle_error(); }
int ch;
while((ch = fgetc(f)) != FEOF && ch != '\n')
{/*empty*/}

// FIXME: needs to handle EOF properly.
child_chunks[child_num].end = ftell(f); // FIXME: needs error check.
child_chunks[child_num+1].start = child_chunks[child_num].end + 1;
spawn_child(child_num);

然后在您的孩子(第 4 步)中,假设孩子可以访问 child_chunks[] 并知道其 child_num

void this_is_the_child(int child_num)
{
    /* ... */

    fseek(f, child_chunks[child_num].start, SEEK_SET); // FIXME: handle error
    while(fgets(...) && ftell(f) < child_chunks[child_num].end)
    {
    }
}

【讨论】:

  • 问题是每一行都有非常不同的大小,但最大是 1024 字节。孩子们必须从每一行读取一个IP,如果我估计错了,他们可能会到达他中间的一个IP,或者其他什么,这当然不可能发生
  • 第 5 步中的第二个项目符号可防止您让估计将一行分成两半 - 向前阅读,直到找到换行符。如果行大小的分布相当均匀,您可以跳过估计预期的行大小,只需将文件长度除以子项数。
  • 你能用一些代码举例说明吗?我的疑问与操作系统概念无关,仅与文件有关:(
【解决方案2】:
/* get an array with line-startpositions (file-offsets) */
fpos_t readLineBegins(FILE *f,fpos_t **begins)
{
  fpos_t ch=0, mark=0, num=0;
  *begins = 0;
  do {
    if( ch=='\n' )
    {
       *begins = realloc( *begins, ++num * sizeof(fpos_t) );
      (*begins)[num-1] = mark;
        mark = ftell(f);
    }
  } while( (ch=fgetc(f))!=EOF );

  if( mark<ftell(f) )
  {
    *begins = realloc( *begins, ++num * sizeof(fpos_t) );
    (*begins)[num-1]=mark;
  }

  return num;
}

/* output linenumber beg...end */
void workLineBlocks(FILE *f,fpos_t *begins,fpos_t beg,fpos_t end)
{
  while( beg<=end )
  {
    int ch;
    fsetpos( f, &begins[beg] ); /* set linestart-position */
    printf("%ld:", ++beg );
    while( (ch=fgetc(f))!=EOF && ch!='\n' && ch!='\r' )
      putchar(ch);
    puts("");
  }
}

main()
{
  FILE *f=fopen("file.txt","rb");
  fpos_t *lineBegins, /* Array with line-startpositions */
  lb = readLineBegins(f,&lineBegins); /* get number of lines */

  workLineBlocks(f,lineBegins,lb-2,lb-1); /* out last two lines */
  workLineBlocks(f,lineBegins,0,1); /* out first two lines */

  fclose(f);
  free(lineBegins);
}

【讨论】:

  • 用相应的包含编译此代码会产生很多错误。你能告诉我你是否测试过它吗?谢谢。
  • 我的 MinGW-GCC 没有任何错误,将 fpos_t 替换为 long like codepad.org/g3OpxZoP
  • 这里是另一个没有编译器错误的更好的解决方案:ideone.com/trPG4
  • 这段代码很棒,但我宁愿有一个存储每一行​​的字符串,而不是放入标准输出。你能告诉我怎么做吗?我厌倦了尝试,但没有成功:(
  • Tks,我已经尝试过了,我已经修改为返回一个 struct 或一个 char* 而不是只打印每一行。如果这比用 fgets 计算总行数更快,然后告诉每个儿子的 beg_line 和 end_line 并让他们每个人做一段时间(fgets)......就像上面的 user476452 所说的那样,你现在是否更快?
【解决方案3】:

我认为它可以帮助你:Read specific range of lines form a text file

【讨论】:

  • 谢谢,我已经编写了与您类似的代码!我计算行数,然后将 min-max 范围划分给每个孩子阅读(每个孩子打开文件)。它可能不是最快的算法(可能是 fseek 和指针),但对我来说更容易理解和调试。
  • 如何知道时间?当我开始 = 时钟 ();在父进程中,启动倒计时时钟,但是当孩子工作时父亲进入睡眠状态,当父进程为 S(睡眠)时时钟暂停。怎样才能让时钟不停止计数?谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-23
相关资源
最近更新 更多