在C控制台程序中读取完整行的最简单方法是什么?输入的文本可能具有可变长度,我们无法对其内容进行任何假设.
您需要动态内存管理,并使用该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; }
注意:永远不要使用获取!它不进行边界检查,可以溢出缓冲区
如果您使用的是GNU C库或其他符合POSIX的库,则可以使用getline()
并传递stdin
给它以获取文件流.
读取静态分配行的一个非常简单但不安全的实现:
char line[1024]; scanf("%[^\n]", line);
一个更安全的实现,没有缓冲区溢出的可能性,但有可能不读取整行,是:
char line[1024]; scanf("%1023[^\n]", line);
声明变量的指定长度与格式字符串中指定的长度之间不是"一个差异".这是一件历史人工制品.
您可能需要使用逐个字符(getc())循环来确保没有缓冲区溢出并且不截断输入.
所以,如果您正在寻找命令参数,请看看Tim的答案.如果您只想从控制台读取一行:
#includeint main() { char string [256]; printf ("Insert your full address: "); gets (string); printf ("Your address is: %s\n",string); return 0; }
是的,它不安全,你可以做缓冲区溢出,它不检查文件的结尾,它不支持编码和许多其他的东西.实际上我甚至没想到它是否做了这些东西.我同意我有点搞砸了:)但是......当我看到一个问题如"如何从C中从控制台读取一行?"时,我认为一个人需要一些简单的东西,比如gets()而不是100行代码像上面一样.实际上,我认为,如果你试图在现实中编写这100行代码,你会犯更多错误,而不是你选择得到的错误;)
getline
可运行的例子
提到这个答案,但这是一个例子.
它是POSIX 7,为我们分配内存,并在循环上很好地重用分配的缓冲区.
指针newbs,读取这个:为什么getline的第一个参数指向指针"char**"而不是"char*"?
#define _XOPEN_SOURCE 700 #include#include 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; }
glibc实现
没有POSIX?也许你想看一下glibc 2.23的实现.
它解析为getdelim
,这是一个getline
带有任意行终止符的简单POSIX超集.
每当需要增加时,它会将分配的内存加倍,并且看起来是线程安全的.
它需要一些宏观扩张,但你不太可能做得更好.
如建议的那样,您可以使用getchar()从控制台读取,直到返回行尾或EOF,从而建立自己的缓冲区。如果您无法设置合理的最大行大小,则会动态增加缓冲区。
您还可以将fgets用作获取以C终止的字符串作为行的安全方法:
#includechar 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);
如果您用尽了控制台输入或由于某种原因操作失败,则返回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'位置)。