file io-如何在C中进行行输入

  • 本文关键字:io- file 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("%sn", line);
} 

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

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

对于来自文件的输入,这段代码运行得无可挑剔。但对于来自stdin的输入,这产生了神秘、怪异、口齿不清的行为。只有在输入第二行之后,才会打印第一行。我无法理解到底发生了什么。

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

然而,在发布这个问题时,我发现了一个更好的替代方案,

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

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

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

为来自文件的输入工作,但是否会导致来自标准输入的输入出现问题?

使用fgets()@FredK

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

尝试使用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当第一次输入时,由于未填充line,以下内容返回0。作为0 != EOF,代码继续使用未初始化的line,从而导致UB。

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

请考虑在以下内容中输入"1234\n"。当第一个fscanf()读到"123"时,可能会出现无限循环,抛出"4",下一个fscanf()调用就会被卡住。

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个字符时,以下操作将失败。因此,满足cal的假设仍然会导致UB。

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

就我个人而言,我认为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()。它还没有经过彻底的测试。请谨慎使用。

相关内容

  • 没有找到相关文章

最新更新