【问题标题】:Can I set unlimited input in C (K&R Exercise 1-16)我可以在 C 中设置无限输入吗(K&R 练习 1-16)
【发布时间】:2021-08-06 08:40:42
【问题描述】:

关于 C 编程语言 (K&R) 的问题 - 练习 1-16

嗨,我是 C 的初学者,我正在阅读 K&R。 我对练习 1-16 有疑问。

修改最长行程序的主程序,使其能够正确打印任意长输入行的长度,并尽可能多地打印文本。

既然我们需要打印“尽可能多的文本”,那么 C 中是否有任何函数可以读取和打印无限输入,以便我可以比较它们并打印最长的输入?

这是书中最长的程序

#include <stdio.h>

#define MAXLINE 1000 /* maximum input line length */

int getline(char line[], int maxline);
void copy(char to[], char from[]);

/* print the longest input line */

main()
{
    int len; /* current line length */
    int max; /* maximum length seen so far */
    char line[MAXLINE]; /* current input line */
    char longest[MAXLINE]; /* current line saved here */

    max = 0;
    while ((len = getline(line, MAXLINE)) > 0)
        if (len > max) {
            max = len;
            copy(longest, line);
        }
    if (max > 0) /* there was a line */
        printf("%s", longest);
    return 0;
}

/* getline: read a line into s, return length */
int getline(char s[], int lim)
{
    int c, i;

    for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
        s[i] = c;
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

/* copy: copy 'from' into 'to'; assume to is big enough */

void copy(char to[], char from[])
{
    int i;

    i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}

【问题讨论】:

  • 您可以分块读取任意长的输入(例如一次 1024 个字符)并计算精确长度。
  • 一种选择是更改 getline 函数,在达到缓冲区大小 lim 时不要停止读取输入,而是继续读取和计数字符,直到 EOF'\n' 不存储他们在缓冲区。通过这种方式,您可以知道实际的行长度,而不会将其限制为缓冲区大小,但是如果行更长,您将只有适合缓冲区的部分。
  • 有一个名为getline 的POSIX 函数可以读取任意长度的行并根据需要分配缓冲区。
  • PO 不需要保存整行,因为问题来自 K&R 的第 1 章,它不假设读者有足够的知识来打印整行。对于无限行,有必要使用两遍算法来记住文件中最长的行所在的位置。将最长的行保存在内存中并在最后完全打印实际上受计算机内存量的限制。原始问题不需要完全打印该行,而只需要尽可能多地打印。

标签: c


【解决方案1】:

使用您在 K&R 问题 1.16 中学习的语言元素,不可能打印最长的行本身,因为您还不知道如何在程序运行时动态地将内存添加到缓冲区(但请继续阅读)

您可以继续读取字符(但不能将它们复制到缓冲区)并增加行长,直到收到来自getchar() 的下一个'\n'EOF 条件。这就是问题说的原因:

...所以它会正确打印任意长输入行的长度,以及尽可能多的文本。

问题的正确解决方案是这样的:

#include <stdio.h>

#define MAXLINE 40 /* longest line size */
/* (1)          ^^ I have changed this to test the 
 * progam with short capacity */

int getlin(char line[], int maxline);
/*  ^^^^^^ I've changed the name of this function, as
 * it clashes with the standard C function getline() */
void copy(char to[], char from[]);

/* print the longest input line */
int main()
/* I have added the type of main, as it is an anachronism
 * to still be using old legacy idioms, and it
 * eliminates a compiler warning */
{
    int len;           /* actual line length */
    int max;           /* max. length up to now. */
    char line[MAXLINE];/* actual line read */
    char longest[MAXLINE]; /* max. line stored here */

    max = 0;
    while ((len = getlin(line, MAXLINE)) >= 0)
        /* (2)                           ^^ original program
         * didn't allow to enter empty lines, as a line should
         * contain at least the '\n' but we are considering that
         * last line could have no final '\n' so we need to check
         * this way. */
        if (len > max) {
            max = len;
            copy(longest, line);
        }
    if (max > 0)    /* we got at least one line */
        printf("%d %s\n", max, longest);
        /* (3)  ^^   ^^   ^^^^ print the length of the longest line
         * and the longest line itself (we add the final \n
         * ourselves, to consider the case the \n cannot be
         * included in the line itself. */
    return 0;
}

/* getlin: read a line in s, return its length */
int getlin(char s[], int lim)
{
    int c, i;
    for (i = 0; /* i < lim-1 && */ (c = getchar()) != EOF && c!='\n'; ++i)
        /*  (4) ^^^^^^^^^^^^^^^^^^ take off this test to continue
         * counting once we get to the maximal length. */
        if (i < lim - 1) /* (5) only add if we have space */
            s[i] = c;
    /* (6) c is always '\n' or EOF on arriving here, so we don't add the
     * final '\n'.  We'll print the lines with a final \n in the
     * above printf() call. ELIMINATE FROM HERE ---->
    if (c == '\n') {
        s[i] = c;
        ++i;
    } ----> ELIMINATED UNTIL HERE */
    if (i < lim-1)
        /* we need to test, as i can be > lim-1 now, we need to set
         * the last char of the string if i < lim-1 and the last
         * char of the buffer in case i >= lim-1 */
        s[i] = '\0';
    else s[lim-1] = '\0';
    if (c == EOF && i == 0)
        return -1;
        /* (7) we reached end of file, and no more data, so we return -1 */
    return i;
}

/* copy: copy 'from' into 'to'; assume that 'to' is large enough */
void copy(char to[], char from[])
{
    int i;

    i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}

程序中的更改已用括号中的数字开头的 cmets 标记,以指示更改。

【讨论】:

    【解决方案2】:
    #define MAXLINE 2000000000
    ...
    char *line    = malloc(MAXLINE); /* current input line */
    char *longest = malloc(MAXLINE); /* current line saved here */
    

    这将限制推高了一点;现在问题出在 getline 中的int lim 参数。

    之前大约是 100 万,可能是堆栈限制。

    这个“无限”是一个棘手的问题,即使您使用库函数(fgets、fscanf 和 'm' 选项)也没有确切的“尽可能”(使用完整的操作系统)。

    你甚至可以扭转它:我可以从文件中读取直到 RAM 已满,交换也可以,并且其他进程被 OOM 杀死?

    我猜这个练习是在考虑缓冲输入,即分块读取行;但是最长的线路在哪里?在一个文件中?使用malloc?这至少会让你有机会检查是否仍然“可能”阅读更多很长的行,或者你是否停下来只保留已经读过的内容。这部分太长的行将是结果 - 找不到更长的

    【讨论】:

      猜你喜欢
      • 2020-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多