如何在C中输入一行?

时间:2016-07-25 20:17:35

标签: c file-io stdin scanf

我试图在C中进行全行输入。最初我做了,

char line[100] // assume no line is longer than 100 letters.
scanf("%s", line);

忽略安全漏洞和缓冲区溢出,我知道这绝不会只是输入一个字。我再次修改了它,

scanf("[^\n]", line);

当然,这不能超过一行输入。但是,下面的代码遇到了无限循环,

while(fscanf(stdin, "%[^\n]", line) != EOF)
{
    printf("%s\n", line);
} 

这是因为\n从未消耗过,并且会在同一点重复停止并在line中具有相同的值。所以我重写了代码,

while(fscanf(stdin, "%[^\n]\n", line) != EOF)
{
    printf("%s\n", line);
}

此代码 工作 无可挑剔(或者我认为),来自文件的输入。但是对于来自stdin的输入,这产生了神秘,奇怪,不明智的行为。只有在输入第二行后,才会打印第一行。我无法理解真正发生的事情。

我所做的就是这个。记下字符串,直到遇到\n,将其存储在line中,然后从输入缓冲区中使用\n。现在打印此line并准备好输入的下一行。还是我被误导了?

在发布这个问题时,我发现了一个更好的选择,

while(fscanf(stdin, "%[^\n]%*c", line) != EOF)
{
    printf("%s\n", line);
}

这适用于所有情况。但我的问题仍然存在。怎么来这个代码,

while(fscanf(stdin, "%[^\n]\n", line) != EOF)
{
    printf("%s\n", line);
}

是否适用于来自文件的输入,但是导致标准输入的输入问题?

2 个答案:

答案 0 :(得分:3)

使用fgets()@FredK

char buf[N];
while (fgets(buf, sizeof buf, stdin)) {
  // crop potential \n if desired.
  buf[strcspn(buf, "\n")] = '\0'; 
  ...
}

尝试将scanf()用于用户输入时会出现许多问题,导致其容易被误用或代码攻击。

// Leaves trailing \n in stdin
scanf("%[^\n]", line)

// Does nothing if line begins with \n. \n remains in stdin
// As return value not checked, use of line may be UB.
// If some text read, consumes \n and then all following whitespace: ' ' \n \t etc.
//    Then does not return until a non-white-space is entered.
//    As stdin is usually buffered, this implies 2 lines of user input.
// Fails to limit input.
scanf("%[^\n]\n", line)

// Does nothing if line begins with \n. \n remains in stdin
// Consumes 1 char after `line`, even if next character is not a \n
scanf("%99[^\n]%*c", line)

检查EOF通常是错误的检查。 @Weather Vane以下内容在\ n first 输入时,返回0,因为line未填充。作为0 != EOF,代码继续使用未初始化的line通往UB。

while(fscanf(stdin, "%[^\n]%*c", line) != EOF)

考虑输入" 1234 \ n"以下。可能是无限循环,如第一个fscanf()读取" 123",抛出" 4"并且下一个fscanf()电话卡在\ n。

while(fscanf(stdin, "%3[^\n]%*c", line) != EOF)

检查*scanf()的结果时,检查您想要的内容,而不是针对您不想要的其中一个值。 (但即使以下还有其他麻烦)

while(fscanf(stdin, "%[^\n]%*c", line) == 1)

关于距行最近的scanf()

char buf[100];
buf[0] = 0;
int cnt = scanf("%99[^\n]", buf);
if (cnt == EOF) Handle_EndOfFile();
// Consume \n if next stdin char is a \n
scanf("%*1[\n]");
// Use buf;
  

while(fscanf(stdin, "%[^\n]%*c", line) != EOF)
  适用于来自文件的输入,但是导致标准输入的输入问题?

发布示例代码和输入/数据文件会很有用。发布适量的代码,有一些潜在的原因。

line超支是UB
输入以\n开头,通向UB
文件或stdin未同时以相同模式打开。 \r未翻译成一个。

注意:当一行为100个字符时,以下操作失败。因此,满足假设仍然会导致UB。

char line[100] // assume no line is longer than 100 letters.
scanf("%s", line);

答案 1 :(得分:0)

就个人而言,我认为fgets()的设计很糟糕。当我读一行时,无论其长度如何,我都希望全部读取它(除了填满所有RAM)。 fgets()不能一气呵成。如果有一条长行,则必须多次手动运行它,直到它到达换行符。在这方面,glibc特定的getline()更方便。这是一个模仿GNU的getline()的函数:

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

long my_getline(char **buf, long *m_buf, FILE *fp)
{
    long tot = 0, max = 0;
    char *p;
    if (*m_buf == 0) { // empty buffer; allocate
        *m_buf = 16;   // initial size; could be larger
        *buf = (char*)malloc(*m_buf); // FIXME: check NULL
    }
    for (p = *buf, max = *m_buf;;) {
        long l, old_m;
        if (fgets(p, max, fp) == NULL)
            return tot? tot : EOF; // reach end-of-file
        for (l = 0; l < max; ++l)
            if (p[l] == '\n') break;
        if (l < max) { // a complete line
            tot += l, p[l] = 0;
            break;
        }
        old_m = *m_buf;
        *m_buf <<= 1; // incomplete line; double the buffer
        *buf = (char*)realloc(*buf, *m_buf); // check NULL
        max = (*m_buf) - old_m;
        p = (*buf) + old_m - 1; // point to the end of partial line
    }
    return tot;
}

int main(int argc, char *argv[])
{
    long l, m_buf = 0;
    char *buf = 0;
    while ((l = my_getline(&buf, &m_buf, stdin)) != EOF)
        puts(buf);
    free(buf);
    return 0;
}

我通常使用自己的readline()函数。我刚才写了这个my_getline()。它尚未经过彻底测试。请谨慎使用。