简体   繁体   English

如何在C中进行行输入?

[英]How to take a line input in C?

I was trying to take a full line input in C. Initially I did, 我试图在C中输入整行。最初我做了,

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

Ignoring security flaws and buffer overflows, I knew this could never take more than a word input. 忽略安全漏洞和缓冲区溢出,我知道这只需要一个字输入。 I modified it again, 我又修改了

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

This, of course, couldn't take more than a line of input. 当然,这不能超过一行输入。 The following code, however was running into infinite loop, 但是,以下代码遇到了无限循环,

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

This was because, the \\n was never consumed, and would repeatedly stop at the same point and had the same value in line . 这是因为\\n从未被消耗过,它会反复在同一点停止并在line具有相同的值。 So I rewrote the code as, 所以我将代码重写为

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

This code worked impeccably(or so I thought), for input from a file. 对于从文件中输入的内容,这段代码运行得非常完美(或者我认为如此)。 But for input from stdin , this produced cryptic, weird, inarticulate behavior. 但是对于来自stdin ,这会产生神秘,怪异,不清晰的行为。 Only after second line was input, the first line would print. 仅在输入第二行之后,才会打印第一行。 I'm unable to understand what is really happening. 我无法理解实际情况。

All I am doing is this. 我要做的就是这个。 Note down the string until you encounter a \\n , store it in line and then consume the \\n from the input buffer. 记下字符串,直到遇到\\n ,将其存储line ,然后从输入缓冲区使用\\n Now print this line and get ready for next line from the input. 现在打印此line并为输入的下一行做好准备。 Or am I being misled? 还是我被误导了?

At the time of posting this question however, I found a better alternative, 但是,在发布此问题时,我发现了一个更好的选择,

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

This works flawlessly for all cases. 这在所有情况下均完美无缺。 But my question still remains. 但是我的问题仍然存在。 How come this code, 这个代码怎么来的

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

worked for inputs from file, but is causing issues for input from standard input? 对于来自文件的输入有效,但是是否导致来自标准输入的输入问题?

Use fgets() . 使用fgets() @FredK @FredK

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

There are to many issues trying to use scanf() for user input that render it prone to mis-use or code attacks. 尝试将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)

Check against EOF is usual the wrong check. 通常,对EOF进行检查是错误的检查。 @Weather Vane The following, when \\n is first entered, returns 0 as line is not populated. @Weather Vane一次输入\\ n时,由于未填充line ,因此返回0。 As 0 != EOF , code goes on to use an uninitialized line leading to UB. 0 != EOF ,代码继续使用通向UB的未初始化line

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

Consider entering "1234\\n" to the following. 考虑在以下输入“ 1234 \\ n”。 Likely infinite loop as first fscanf() read "123", tosses the "4" and the next fscanf() call gets stuck on \\n. 当第一个fscanf()读取“ 123”,扔掉“ 4”,下一个fscanf()调用陷入\\ n时,可能会发生无限循环。

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

When checking the results of *scanf() , check against what you want , not against one of the values you do not want. 在检查*scanf()的结果时, 请检查所需的内容 ,而不要检查不需要的值之一。 (But even the following has other troubles) (但即使以下情况也有其他麻烦)

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

About the closest scanf() to read a line : 关于最接近的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)
worked for inputs from file, but is causing issues for input from standard input? 对于来自文件的输入有效,但是是否导致来自标准输入的输入问题?

Posting sample code and input/data file would be useful. 发布示例代码和输入/数据文件将很有用。 With modest amount of code posted, some potential reasons. 在发布少量代码的情况下,可能存在某些原因。

line overrun is UB line超限是UB
Input begins with \\n leading to UB 输入以\\n开头到UB
File or stdin not both opened in same mode. 文件或stdin都不以相同模式打开。 \\r not translated in one. \\r不能翻译成一个。


Note: The following fails when a line is 100 characters. 注意:当一行为100个字符时,以下操作失败。 So meeting the assumption cal still lead to UB. 因此,满足假设cal仍然会导致UB。

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

Personally, I think fgets() is badly designed. 就个人而言,我认为fgets()的设计不正确。 When I read a line, I want to read it in whole regardless of its length (except filling up all RAM). 当我读取一行时,无论长度如何,我都希望完整读取它(填充所有RAM除外)。 fgets() can't do that in one go. fgets()不能一that而就。 If there is a long line, you have to manually run it multiple times until it reaches the newline. 如果行很长,则必须多次手动运行它,直到到达换行符为止。 The glibc-specific getline() is more convenient in this regard. 在这方面,特定于glibc的getline()更方便。 Here is a function that mimics GNU's 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;
}

I usually use my own readline() function. 我通常使用自己的readline()函数。 I wrote this my_getline() a moment ago. 我刚才写了这个my_getline() It has not been thoroughly tested. 尚未经过全面测试。 Please use with caution. 请谨慎使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM