在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;
}
gets()
!它不会进行边界检查,可能会导致缓冲区溢出。char line[1024];
scanf("%[^\n]", line);
char line[1024];
scanf("%1023[^\n]", line);
所以,如果您正在寻找命令参数,请查看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;
}
是的,它不安全,可能会发生缓冲区溢出,它不检查文件结尾,也不支持编码和许多其他功能。 事实上,我甚至没有考虑它是否具备这些功能。 我同意,我有点犯了错误 :) 但是......当我看到像“如何在C中从控制台读取一行?”这样的问题时,我认为一个人需要简单的东西,比如gets()而不是像上面那样100行的代码。 实际上,我认为,如果你试着在现实中编写那100行的代码,你会犯更多的错误,而如果你选择了gets,你会少犯一些错误;)
gets
已经不存在了,所以在 C11 中这个方法行不通。 - Antti Haapala -- Слава Україніgetline
可运行示例
getline
被提及 在这个答案中,但这里提供一个示例。
它是POSIX 7标准函数,为我们分配内存,并在循环中漂亮地重用已分配的缓冲区。
对于指针初学者,阅读此文:为什么getline的第一个参数是指向指针char**而不是char *?
main.c
#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 (1) {
puts("enter a line");
read = getline(&line, &len, stdin);
if (read == -1)
break;
printf("line = %s", line);
printf("line length = %zu\n", read);
puts("");
}
free(line);
return 0;
}
编译并运行:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
结果:这会显示在终端上:
enter a line
然后如果您输入:
asdf
输入并按下回车键,将会出现以下内容:
line = asdf
line length = 5
接着是另一个:
enter a line
或者从管道输入到标准输入:
printf 'asdf\nqwer\n' | ./main.out
提供:
enter a line
line = asdf
line length = 5
enter a line
line = qwer
line length = 5
enter a line
在Ubuntu 20.04上进行测试。
glibc实现
没有POSIX?也许你想看看glibc 2.23实现。
它解析为getdelim
,这是一个简单的POSIX超集,具有任意行终止符。
每当需要增加时,它会将分配的内存翻倍,并且看起来是线程安全的。
它需要一些宏扩展,但你不太可能做得更好。
len
的目的是什么,当读取时也提供了长度? - Honinbo Shusakuman getline
。len
是现有缓冲区的长度,0
是魔法数字,表示要分配内存。read
是读取的字符数。缓冲区大小可能大于read
。 - Ciro Santilli OurBigBook.com您可能需要使用字符循环(getc())来确保没有缓冲区溢出并且不截断输入。
建议使用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);
如何在C中从控制台读取一行?
构建自己的函数是实现从控制台读取一行的方法之一。
我使用动态内存分配来分配所需的内存量。
当我们即将耗尽分配的内存时,我们尝试将内存大小加倍。
在这里,我使用循环逐个扫描字符串中的每个字符,使用getchar()
函数,直到用户输入'\n'
或EOF
字符。
最后,在返回该行之前,我们删除任何额外分配的内存。
//the function to read lines of variable length
char* scan_line(char *line)
{
int ch; // as getchar() returns `int`
long capacity = 0; // capacity of the buffer
long length = 0; // maintains the length of the string
char *temp = NULL; // use additional pointer to perform allocations in order to avoid memory leaks
while ( ((ch = getchar()) != '\n') && (ch != EOF) )
{
if((length + 1) >= capacity)
{
// resetting capacity
if (capacity == 0)
capacity = 2; // some initial fixed length
else
capacity *= 2; // double the size
// try reallocating the memory
if( (temp = realloc(line, capacity * sizeof(char))) == NULL ) //allocating memory
{
printf("ERROR: unsuccessful allocation");
// return line; or you can exit
exit(1);
}
line = temp;
}
line[length] = (char) ch; //type casting `int` to `char`
length++;
}
line[length + 1] = '\0'; //inserting null character at the end
// remove additionally allocated memory
if( (temp = realloc(line, (length + 1) * sizeof(char))) == NULL )
{
printf("ERROR: unsuccessful allocation");
// return line; or you can exit
exit(1);
}
line = temp;
return line;
}
Now you could read a full line this way :
char *line = NULL;
line = scan_line(line);
scan_line()
函数的 示例程序:#include <stdio.h>
#include <stdlib.h> //for dynamic allocation functions
char* scan_line(char *line)
{
..........
}
int main(void)
{
char *a = NULL;
a = scan_line(a); //function call to scan the line
printf("%s\n",a); //printing the scanned line
free(a); //don't forget to free the malloc'd pointer
}
示例输入:
Twinkle Twinkle little star.... in the sky!
样例输出:
Twinkle Twinkle little star.... in the sky!
scanf内可以使用类似于正则表达式的语法来接收整行输入:
scanf("%[^\n]%*c", str);
^\n表示读取直到遇到换行符。然后,使用 %*c 读取换行符,并且 * 表示该换行符被丢弃。
示例代码:
#include <stdio.h>
int main()
{
char S[101];
scanf("%[^\n]%*c", S);
printf("%s", S);
return 0;
}
scanf_s()
而不是 scanf()
,则需要提供第三个参数。最安全的命令应该是 scanf_s("%[^\n]%*c", S, (unsigned)_countof(S));
。 - Ronald Souza我之前遇到过同样的问题,这是我的解决方案,希望能帮到你。
/*
* Initial size of the read buffer
*/
#define DEFAULT_BUFFER 1024
/*
* Standard boolean type definition
*/
typedef enum{ false = 0, true = 1 }bool;
/*
* Flags errors in pointer returning functions
*/
bool has_err = false;
/*
* Reads the next line of text from file and returns it.
* The line must be free()d afterwards.
*
* This function will segfault on binary data.
*/
char *readLine(FILE *file){
char *buffer = NULL;
char *tmp_buf = NULL;
bool line_read = false;
int iteration = 0;
int offset = 0;
if(file == NULL){
fprintf(stderr, "readLine: NULL file pointer passed!\n");
has_err = true;
return NULL;
}
while(!line_read){
if((tmp_buf = malloc(DEFAULT_BUFFER)) == NULL){
fprintf(stderr, "readLine: Unable to allocate temporary buffer!\n");
if(buffer != NULL)
free(buffer);
has_err = true;
return NULL;
}
if(fgets(tmp_buf, DEFAULT_BUFFER, file) == NULL){
free(tmp_buf);
break;
}
if(tmp_buf[strlen(tmp_buf) - 1] == '\n') /* we have an end of line */
line_read = true;
offset = DEFAULT_BUFFER * (iteration + 1);
if((buffer = realloc(buffer, offset)) == NULL){
fprintf(stderr, "readLine: Unable to reallocate buffer!\n");
free(tmp_buf);
has_err = true;
return NULL;
}
offset = DEFAULT_BUFFER * iteration - iteration;
if(memcpy(buffer + offset, tmp_buf, DEFAULT_BUFFER) == NULL){
fprintf(stderr, "readLine: Cannot copy to buffer\n");
free(tmp_buf);
if(buffer != NULL)
free(buffer);
has_err = true;
return NULL;
}
free(tmp_buf);
iteration++;
}
return buffer;
}
goto
来处理错误情况,您的代码将变得简单得多。尽管如此,您是否认为在循环中重复使用tmp_buf
而不是每次都使用相同大小的malloc
会更好呢? - Shahbazhas_err
报告错误使得此函数线程不安全且不够方便易用。请不要这样做。通过返回 NULL 已经可以指示错误了。此外,可以考虑在通用的库函数中不打印错误消息。 - Jonathan Leffler
fgetc_unlocked
。 - vladrfgetc(stdin)
可能会返回EOF
,特别是当stdin
从文件中进行管道传输或用户按下关闭stdin
的键组合时(例如在 Linux 上按下 CTRL+d 或在 Windows 上按下 CTRL+z)。 - autisticgetline()
函数与POSIX标准中的getline()
函数不同。 - Jonathan Leffler