代码之家  ›  专栏  ›  技术社区  ›  Tool

打印输入的最后n行

  •  2
  • Tool  · 技术社区  · 14 年前
    #include <stdio.h>
    
    #define MAXLINES    5000    /* Maximum number of lines to display. */
    
    char *lineptr[MAXLINES];    /* Pointer to input lines. */
    
    #define BUFFERSIZE 1000
    
    #define DEFAULT_LAST 10
    
    int readlines(char *lineptr[], char *buffer, int maxlines);
    static void unwrap(char *buffer, int index);
    static void reverse(char *lineptr[], int nlines);
    
    main(int argc, char *argv[])
    {
        int nlines, i, last, offset;
        char buffer[BUFFERSIZE];
        char *p;
    
        last = DEFAULT_LAST;
        for (i = 0; i < argc; i++) {
            p = argv[i];
            if (*p++ == '-') {
                last = 0;
                while (isdigit(*p)) {
                    last = last * 10 + *p - '0';
                    p++;
                }
                if (*p != '\0') {
                    printf("invalid argument: %s\n", argv[i]);
                    last = DEFAULT_LAST;
                }
            }
        }
    
        nlines = readlines(lineptr, buffer, MAXLINES);
        if (nlines < 0) {
            printf("error: input too big to process\n");
            return 1;
        }
        if (nlines < last) {
            printf("error: only printing the last %d lines.\n", nlines);
            offset = 0;
        } else if (last > MAXLINES) {
            offset = nlines - MAXLINES;
        } else {
            offset = nlines - last;
        }
        for (i = 0; i < nlines && i < last; i++)
            printf("%s\n", lineptr[offset + i]);
    
        return 0;
    }
    
    int readlines(char *lineptr[], char *buffer, int maxlines)
    {
        int c, nlines;
        int wrapped;
        char *p;
    
        /* The input lines are stored end-to-end in the buffer, with
           newlines converted to null bytes. */
        wrapped = 0;
        p = buffer;
        while ((c = getchar()) != EOF) {
            if (c == '\n')
                *p = '\0';
            else
                *p = c;
            p++;
            if (p >= buffer + BUFFERSIZE) {
                p = buffer;
                wrapped = 1;
            }
        }
        /* Rearrange the buffer so the oldest byte comes first. */
        if (wrapped) {
            unwrap(buffer, p - buffer);
            p = buffer + BUFFERSIZE;
        }
        p--;
        *p = '\0';
        nlines = 0;
        while (p >= buffer && nlines < maxlines) {
            p--;
            if (*p == '\0')
                lineptr[nlines++] = p + 1;
        }
        reverse(lineptr, nlines);
    
        return nlines;
    }
    
    static void unwrap(char *buffer, int index)
    {
        char work[BUFFERSIZE];
    
        memmove(work, buffer + index, BUFFERSIZE - index);
        memmove(work + BUFFERSIZE - index, buffer, index);
        memmove(buffer, work, BUFFERSIZE);
    
        return;
    }
    
    static void reverse(char *lineptr[], int nlines)
    {
        char *tmp;
        int i;
    
        for (i = 0; i < nlines / 2; i++) {
            tmp = lineptr[i];
            lineptr[i] = lineptr[nlines - i - 1];
            lineptr[nlines - i - 1] = tmp;
        }
    return;
    }
    

    这个程序打印最后n行输入,将行存储到指针数组中。

    在readlines函数中,如果指向缓冲区的指针超过了它的最大大小,它就会被包装起来。但我不明白包装/展开功能究竟做了什么。有人能给我解释一下吗?Wrap的工作方式,以及如果缓冲区溢出,为什么该代码的编写者不返回-1?

    2 回复  |  直到 14 年前
        1
  •  5
  •   Matthew Slattery    14 年前

    为了演示这个原理:假设您使用相同的方案将10个字符“0”到“9”放入8字节缓冲区:

    7个字符后:

    +---+---+---+---+---+---+---+---+
    | 0 | 1 | 2 | 3 | 4 | 5 | 6 |   |
    +---+---+---+---+---+---+---+---+
      ^                           ^
    buffer                        p
    

    在第8个字符之后:

    +---+---+---+---+---+---+---+---+
    | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
    +---+---+---+---+---+---+---+---+
      ^                               ^
    buffer                            p
    

    所以现在 p 重置和 wrapped 设置为1:

    +---+---+---+---+---+---+---+---+
    | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
    +---+---+---+---+---+---+---+---+
      ^
    buffer
      p
    

    在第10个字符之后:

    +---+---+---+---+---+---+---+---+
    | 8 | 9 | 2 | 3 | 4 | 5 | 6 | 7 |
    +---+---+---+---+---+---+---+---+
      ^       ^
    buffer    p
    

    现在 unwrap() 代码重新排列缓冲区,如下所示:

    +---+---+---+---+---+---+---+---+
    | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
    +---+---+---+---+---+---+---+---+
      ^                               ^
    buffer                            p
    

    程序正在这样做(而不仅仅是放弃),这样即使文件比缓冲区大很多,它仍然可以工作。(除非最后10行的总长度大于缓冲区,in 在这种情况下,最后10行中的一些行会丢失)。

        2
  •  0
  •   Hogan    14 年前

    这个程序将所有行读入一个行数组。数组中的每个元素都有固定的大小。如果一行的长度超过了该行的最大大小,它将“包装”该行,并在缓冲区开始处重新开始填充缓冲区。

    然后展开将最旧的内容放在末尾,这样行看起来就从行的开头截断了。(10个字符缓冲区中的12个字符行将显示从第3个字符开始的最后10个字符。)