如何从C中的控制台读取一行?

pbr*_*ult 98 c io console stdin readline

在C控制台程序中读取完整行的最简单方法是什么?输入的文本可能具有可变长度,我们无法对其内容进行任何假设.

Joh*_*itb 76

您需要动态内存管理,并使用该fgets功能读取您的行.但是,似乎没有办法看到它读取了多少个字符.所以你使用fgetc:

char * getline(void) {
    char * line = malloc(100), * linep = line;
    size_t lenmax = 100, len = lenmax;
    int c;

    if(line == NULL)
        return NULL;

    for(;;) {
        c = fgetc(stdin);
        if(c == EOF)
            break;

        if(--len == 0) {
            len = lenmax;
            char * linen = realloc(linep, lenmax *= 2);

            if(linen == NULL) {
                free(linep);
                return NULL;
            }
            line = linen + (line - linep);
            linep = linen;
        }

        if((*line++ = c) == '\n')
            break;
    }
    *line = '\0';
    return linep;
}
Run Code Online (Sandbox Code Playgroud)

注意:永远不要使用获取!它不进行边界检查,可以溢出缓冲区

  • 你可以通过使用缓冲区执行fgets并检查最后是否有换行符来提高效率.如果不这样做,请重新分配累积缓冲区,将其复制到fgets中,然后重新进行fgets. (4认同)
  • 此功能需要更正:行"len = lenmax;" realloc应该在realloc之前或者应该是"len = lenmax >> 1"; - 或其他一些等同于已经使用了一半长度的事实. (3认同)
  • 请注意,这个`getline()`与POSIX标准[`getline()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html)函数不同. (2认同)

dmi*_*gov 27

如果您使用的是GNU C库或其他符合POSIX的库,则可以使用getline()并传递stdin给它以获取文件流.


小智 15

读取静态分配行的一个非常简单但不安全的实现:

char line[1024];

scanf("%[^\n]", line);
Run Code Online (Sandbox Code Playgroud)

一个更安全的实现,没有缓冲区溢出的可能性,但有可能不读取整行,是:

char line[1024];

scanf("%1023[^\n]", line);
Run Code Online (Sandbox Code Playgroud)

声明变量的指定长度与格式字符串中指定的长度之间不是"一个差异".这是一件历史人工制品.

  • **这根本不安全.它有完全相同的问题为什么`gets`被从标准中删除** (13认同)
  • **主持人注意:**上面的评论指的是[答案的先前版本。](https://stackoverflow.com/posts/318799/revisions) (3认同)

Tim*_*Tim 11

您可能需要使用逐个字符(getc())循环来确保没有缓冲区溢出并且不截断输入.


Pau*_*tin 10

所以,如果您正在寻找命令参数,请看看Tim的答案.如果您只想从控制台读取一行:

#include <stdio.h>

int main()
{
  char string [256];
  printf ("Insert your full address: ");
  gets (string);
  printf ("Your address is: %s\n",string);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

是的,它不安全,你可以做缓冲区溢出,它不检查文件的结尾,它不支持编码和许多其他的东西.实际上我甚至没想到它是否做了这些东西.我同意我有点搞砸了:)但是......当我看到一个问题如"如何从C中从控制台读取一行?"时,我认为一个人需要一些简单的东西,比如gets()而不是100行代码像上面一样.实际上,我认为,如果你试图在现实中编写这100行代码,你会犯更多错误,而不是你选择得到的错误;)

  • 另一方面,如果您正在为自己编写程序并且只需要阅读输入,那么这非常好.程序需要多少安全性才符合规范 - 您不必每次都将其作为优先事项. (5认同)
  • @Tim - 我想保留所有的历史:) (4认同)
  • Downvoted.`gets`不再存在,因此这在C11中不起作用. (3认同)
  • -1,不应使用gets(),因为它不进行边界检查. (2认同)
  • 如果这种情况发生在生产环境附近的任何地方,最终用户要么会受到攻击,要么会遭到黑客攻击。这不是一个好用的功能。 (2认同)

Cir*_*四事件 8

getline 可运行的例子

提到这个答案,但这是一个例子.

它是POSIX 7,为我们分配内存,并在循环上很好地重用分配的缓冲区.

指针newbs,读取这个:为什么getline的第一个参数指向指针"char**"而不是"char*"?

#define _XOPEN_SOURCE 700

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

int main(void) {
    char *line = NULL;
    size_t len = 0;
    ssize_t read = 0;
    while (read != -1) {
        puts("enter a line");
        read = getline(&line, &len, stdin);
        printf("line = %s", line);
        printf("line length = %zu\n", read);
        puts("");
    }
    free(line);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

glibc实现

没有POSIX?也许你想看一下glibc 2.23的实现.

它解析为getdelim,这是一个getline带有任意行终止符的简单POSIX超集.

每当需要增加时,它会将分配的内存加倍,并且看起来是线程安全的.

它需要一些宏观扩张,但你不太可能做得更好.


orc*_*mid 5

如建议的那样,您可以使用getchar()从控制台读取,直到返回行尾或EOF,从而建立自己的缓冲区。如果您无法设置合理的最大行大小,则会动态增加缓冲区。

您还可以将fgets用作获取以C终止的字符串作为行的安全方法:

#include <stdio.h>

char line[1024];  /* Generously large value for most situations */

char *eof;

line[0] = '\0'; /* Ensure empty line if no input delivered */
line[sizeof(line)-1] = ~'\0';  /* Ensure no false-null at end of buffer */

eof = fgets(line, sizeof(line), stdin);
Run Code Online (Sandbox Code Playgroud)

如果您用尽了控制台输入或由于某种原因操作失败,则返回eof == NULL,并且行缓冲区可能保持不变(这就是将第一个字符设置为'\ 0'的原因)。

fgets不会使line []溢出,并且将确保在成功返回时,最后一个可接受的字符之后为null。

如果到达了行尾,则终止符'\ 0'之前的字符将是'\ n'。

如果在结尾“ \ 0”之前没有终止符“ \ n”,则可能是有更多数据,或者下一个请求将报告文件结束。您必须做另一个fgets来确定哪个。(就此而言,使用getchar()循环更容易。)

在上面的(更新的)示例代码中,如果成功执行fget之后,如果line [sizeof(line)-1] =='\ 0',则说明缓冲区已完全填满。如果该职位以'\ n'开头,则说明您很幸运。否则,标准输入中可能有更多数据或文件末尾。(当缓冲区未完全填满时,您可能仍位于文件末尾,并且当前行的末尾也可能没有'\ n'。由于您必须扫描字符串才能查找和/或删除字符串末尾(缓冲区的第一个'\ 0')之前的任何'\ n',我倾向于首先使用getchar()。)

做您需要做的事情以处理仍然多于您作为第一块读取的行数的行。可以使动态增长缓冲区的示例与getchar或fgets一起使用。有一些棘手的边缘情况需要提防(例如,记住要在缓冲区扩展之前,将下一个输入开始存储在结束先前输入的'\ 0'位置)。