【问题标题】:How would you implement tail efficiently?你将如何有效地实现tail?
【发布时间】:2012-04-27 05:17:33
【问题描述】:

在*NIX 中实现tail 的有效方法是什么? 我想出了(写)两个简单的解决方案,都使用一种循环缓冲区将行加载到循环结构中(数组|双向链接循环列表-为了好玩)。 我在busybox中看到了部分较旧的实现,据我所知,他们使用 fseek 来查找EOF,然后“向后”读取内容。那里有什么更清洁、更快的东西吗? 我在面试时被问到这个问题,提问者看起来并不满意。提前谢谢你。

【问题讨论】:

  • 我喜欢这个问题,因为这是学习编程(以及一般的系统知识)时非常重要的一课。有些操作本质上不可能高效地完成,至少没有给出您正在使用的数据的标准表示(在这种情况下,是从头开始的线性字节流文件)。学习简单地从数据的格式中识别这一点,并避免将无法有效协同工作的数据和操作配对,是学习编写高效软件的关键部分。

标签: c linux unix tail


【解决方案1】:

我认为没有什么不同于“在向前读取数据时保留最新的 N 行”或“从末尾开始并向后直到读取第 N 行”不同的解决方案。

关键是你会根据上下文使用一个或另一个。

当tail访问一个随机访问文件,或者当数据小到可以放入内存时,“走到尽头并向后退”更好。 在这种情况下,运行时间被最小化,因为您扫描了必须输出的数据(因此,它是“最佳的”)

当尾部由管道馈送或数据很大时,您的解决方案(保留 N 个最新行)会更好。 在这种情况下,其他解决方案会浪费太多内存,因此不实用,并且在源比 tail 慢的情况下(很可能)扫描所有文件也没那么重要。

【讨论】:

    【解决方案2】:

    从文件末尾向后读取,直到读取N 换行符或到达文件开头。

    然后打印刚刚阅读的内容。

    我认为这里不需要任何花哨的数据结构。

    Here is the source code of tail如果你有兴趣。

    【讨论】:

      【解决方案3】:

      首先使用fseek 查找文件结尾,然后将512 和fseek 减去该偏移量,然后从那里向前读取到结尾。计算换行符的数量,因为如果换行符太少,您将不得不做同样的事情,减去 1024 的偏移量 ... 但在 99% 的情况下,512 就足够了。

      这个 (1) 避免了向前读取整个文件,而 (2) 这可能比从末尾向后读取更有效的原因是向前读取是通常更快。

      【讨论】:

      • 每次失败时将偏移量加倍。
      【解决方案4】:

      /*This example implements the option n of tail command.*/

      #define _FILE_OFFSET_BITS 64
      #include <stdio.h>
      #include <stdlib.h>
      #include <fcntl.h>
      #include <errno.h>
      #include <unistd.h>
      #include <getopt.h>
      
      #define BUFF_SIZE 4096
      
      FILE *openFile(const char *filePath)
      {
        FILE *file;
        file= fopen(filePath, "r");
        if(file == NULL)
        {
          fprintf(stderr,"Error opening file: %s\n",filePath);
          exit(errno);
        }
        return(file);
      }
      
      void printLine(FILE *file, off_t startline)
      {
        int fd;
        fd= fileno(file);
        int nread;
        char buffer[BUFF_SIZE];
        lseek(fd,(startline + 1),SEEK_SET);
        while((nread= read(fd,buffer,BUFF_SIZE)) > 0)
        {
          write(STDOUT_FILENO, buffer, nread);
        }
      }
      
      void walkFile(FILE *file, long nlines)
      {
        off_t fposition;
        fseek(file,0,SEEK_END);
        fposition= ftell(file);
        off_t index= fposition;
        off_t end= fposition;
        long countlines= 0;
        char cbyte;
      
        for(index; index >= 0; index --)
        {
          cbyte= fgetc(file);
          if (cbyte == '\n' && (end - index) > 1)
          {
            countlines ++;
            if(countlines == nlines)
            {
          break;
            }
           }
          fposition--;
          fseek(file,fposition,SEEK_SET);
        }
        printLine(file, fposition);
        fclose(file);
      }
      
      int main(int argc, char *argv[])
      {
        FILE *file;
        file= openFile(argv[2]);
        walkFile(file, atol(argv[1]));
        return 0;
      }
      
      /*Note: take in mind that i not wrote code to parse input options and arguments, neither code to check if the lines number argument is really a number.*/
      

      【讨论】:

        猜你喜欢
        • 2010-12-14
        • 1970-01-01
        • 2012-11-17
        • 1970-01-01
        • 1970-01-01
        • 2012-02-24
        • 1970-01-01
        • 2010-09-19
        • 1970-01-01
        相关资源
        最近更新 更多