K&R 练习 5-13 如何让我的程序处理无限行的输入

K&R exercise 5-13 How to make my program handle infinite lines of input

提问人:hansoko 提问时间:9/8/2023 更新时间:9/9/2023 访问量:52

问:

练习 5-13。编写程序尾部,打印最后 n 行 的输入。默认情况下,n 设置为 10,让我们说,但它可以是 由可选参数更改,以便打印最后 n 线。无论多么不合理,程序都应该理性地行事 n 的输入或值。编写程序,使其达到最佳效果 使用可用存储;行应按排序方式存储 第 5.6 节的程序,而不是固定大小的二维数组。tail -n

这是我目前编写的代码:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

int isint(char s[]);
int toint(char s[]);
int readlines(char *lineptr[], int no);
void writelines(char *lineptr[], int no);

#define MAXLINE 100

int main(int argc, char *argv[])
{
    int n = 10, nlines;
    char *lineptr[MAXLINE];

    while (--argc > 0) {
        if ((*++argv)[0] == '-' && isint(*argv) && toint(*argv) <= MAXLINE) {
            n = toint(*argv);
            break;
        }
        else
            printf("invalid argument %s\n", *argv);
    }

    printf("Printing %d line%s\n", n, n > 1 ? "s" : "");
    if ((nlines = readlines(lineptr, n)) >= 0) {
        writelines(lineptr, nlines);
        return 0;
    }
    else {
        printf("input too big too handle\n");
        return 1;
    }

    return 0;
}

int isint(char s[])
{
    while (*s == '-' || *s == '0')
        s++;
    if (isdigit(*s)) {
        while (*s != '\0')
            if (!isdigit(*s++))
                return 0;
        return 1;
    }
    else
        return 0;
}

int toint(char s[])
{
    while (*s == '-' || *s == '0')
        s++;
    return atoi(s);
}

#define MAXLEN 1000

int readlines(char *lineptr[], int no)
{
    char *alloc(int n, int no);
    int get_line(char *, int);
    void push(char *lineptr[], char *p, int len);

    int nlines, len, i;
    char *p, line[MAXLEN];

    for (i = 0; i < no; i++)
        lineptr[i] = NULL;

    nlines = 0;
    while ((len = get_line(line, MAXLEN)) > 0)
        if ((p = alloc(len, no)) == NULL) {
            return -1;
        } else {
            line[len-1] = '\0';
            strcpy(p, line);
            push(lineptr, p, no);
            nlines++;
        }
    return no < nlines ? no : nlines;
}

void writelines(char *lineptr[], int no)
{
    int i;
    
    for (i = 0; i < no; i++)
        if (lineptr[i] != NULL)
            printf("%s\n", lineptr[i]);
}

int get_line(char *line, int lim)
{
    int i, c;

    i = 0;
    while (--lim > 0 && (c = getchar()) != EOF && c != '\n')
        line[i++] = c;
    if (c == '\n')
        line[i++] = c;
    line[i] = '\0';

    return i;
}

void push(char *lineptr[], char *p, int no)
{
    void shift(int i)
    {
        if (i > 1)
            shift(i-1);
        lineptr[i-1] = lineptr[i];
    }

    shift(no-1);
    lineptr[no-1] = p;
}

static char allocbuf[MAXLINE][MAXLEN];
static int allocn = 0;

char *alloc(int n, int no)
{
    if (n < MAXLEN) {
        allocn++;
        return allocbuf[allocn%no];
    } else
        return NULL;
}

它可以处理无限数量的行作为输入,但它使用二维数组,本书要求我像这样存储这些行:

#define ALLOCSIZE 10000

static char allocbuf[ALLOCSIZE];
static char *allocp = allocbuf;

char *alloc(int n)
{
    if (n <= ALLOCSIZE - (allocp - allocbuf)) {
        allocp += n;
        return allocp - n;
    } else
        return NULL;
}

void afree(char *p)
{
    if (p >= allocbuf && p < allocbuf + ALLOCSIZE)
        allocp = p;
}

这样,我就无法用新行替换已存储的过时行。作为解决方案,我可以拆分成多个 1000 个字符长的部分,但这本质上是一个手动二维数组和重新发明轮子,而不是书中要求我做的事情。我只能想到少数几个设计,这些设计真的很难实现。allocbuf

C 指针 分配

评论

0赞 Jason 9/8/2023
你能不能只是寻找/阅读文件,直到你到达正确的起点,然后从那个点开始写?
0赞 Ian Abbott 9/8/2023
请注意,标准 C 不支持嵌套函数定义,例如在函数中定义的函数。shift()push()
0赞 hansoko 9/8/2023
@Jason 本书目前还没有介绍如何处理文件,我只能使用 getchar,因此无法知道输入有多长。
0赞 hansoko 9/8/2023
@IanAbbott 注意到了,谢谢你告诉我,但我想 gcc 可以编译它
0赞 Craig Estey 9/9/2023
本书(第 91 页)还希望您这样做:存储指向可变长度线的指针。char *lineptr[MAXLINES];

答:

1赞 hansoko 9/9/2023 #1

看起来有点乱,但我想通了:

#define ALLOCSIZE MAXLINE * MAXLEN

static char allocbuf[ALLOCSIZE];
static int allocn = 0; /* the number of allocations */
static char *allocp = allocbuf;
static char *backallocp = allocbuf; /* the start of replaceable section of array */
static char *backallocpend = allocbuf - 1; /* the end of replaceable section of array */

char *alloc(int n, int no)
{
    int i;
    char *j;

    if (allocn%no == no - 1) {
        backallocp = allocbuf;
        backallocpend = allocbuf - 1;
    }
    if (allocn >= no) {
        while (allocn-no >= no)
            allocn -= no;
        for (i = 0; i < allocn%no; i++)
            while (*backallocpend++ != '\0');
                ;
    }

    if (n <= backallocpend - backallocp + 1) {
        backallocp += n;
        allocn++;
        return backallocp - n;
    } else if (n <= allocbuf + ALLOCSIZE - allocp) {
        allocp += n;
        allocn++;
        return allocp - n;
    } else
        return NULL;
}