繁体   English   中英

使用 fgets 从 c 中的 stdin 读取未知长度的行

[英]Reading an unknown length line from stdin in c with fgets

我正在尝试使用 C 语言从 stdin 读取未知长度的行。

我在网上看的时候看到过这个:

char** str;
gets(&str);

但这似乎给我带来了一些问题,我真的不明白如何以这种方式做到这一点。

你能解释一下为什么这个例子有效/无效以及实现它的正确方法是什么(使用 malloc?)

你不想要一个指向char指针的指针,使用一个char数组

char str[128];

或指向char的指针

char *str;

如果选择指针,则需要使用malloc保留空间

str = malloc(128);

然后你可以使用fgets

fgets(str, 128, stdin);

并删除尾随的换行符

char *ptr = strchr(str, '\n');
if (ptr != NULL) *ptr = '\0';

要读取任意长行,您可以使用getline (添加到 GNU 版本的 libc 的函数):

#define _GNU_SOURCE
#include <stdio.h>

char *foo(FILE * f)
{
    int n = 0, result;
    char *buf;

    result = getline(&buf, &n, f);
    if (result < 0) return NULL;
    return buf;
}

或使用fgetsrealloc自己的实现:

char *getline(FILE * f)
{
    size_t size = 0;
    size_t len  = 0;
    size_t last = 0;
    char *buf = NULL;

    do {
        size += BUFSIZ; /* BUFSIZ is defined as "the optimal read size for this platform" */
        buf = realloc(buf, size); /* realloc(NULL,n) is the same as malloc(n) */            
        /* Actually do the read. Note that fgets puts a terminal '\0' on the
           end of the string, so we make sure we overwrite this */
        if (buf == NULL) return NULL;
        fgets(buf + last, BUFSIZ, f);
        len = strlen(buf);
        last = len - 1;
    } while (!feof(f) && buf[last] != '\n');
    return buf;
}

调用它使用

char *str = getline(stdin);

if (str == NULL) {
    perror("getline");
    exit(EXIT_FAILURE);
}
...
free(str);

更多信息

首先, gets()没有提供防止缓冲区溢出的方法。 这使得它非常危险,它已从最新的 C 标准中删除。 不应该使用它。 但是,通常的用法类似于

char buffer[20];
gets(buffer);      /*  pray that user enters no more than 19 characters in a line */

您的用法是将gets()传递给指向char 指针的指针。 这不是gets()期望的,因此您的代码甚至无法编译。

评论中反映的祈祷元素就是为什么gets()如此危险。 如果用户输入 20 个(或更多)字符, gets()会很高兴地将数据写入buffer的末尾。 程序员无法在代码中阻止这种情况(除非访问硬件以电击输入过多数据的用户,这超出了标准 C 的范围)。

但是,要回答您的问题,唯一的方法涉及分配某个大小的缓冲区,以某种受控方式读取数据直到达到该大小,如果需要重新分配以获得更大的大小,并继续直到换行(或结束)文件,或其他一些输入错误条件)。

malloc()可用于初始分配。 malloc()realloc()可用于重新分配(如果需要)。 请记住,当不再需要数据时,必须释放以这种方式分配的缓冲区(使用free() ) - 否则结果是内存泄漏。

使用 getline() 函数,这将返回行的长度,以及指向已分配内存区域中该行内容的指针。 (确保在完成后将行指针传递给 free() )

“使用 fgets 从 c 中的 stdin 读取未知长度的行”

延迟响应 - Windows 方法:

OP 没有指定 Linux 或 Windows,但针对这个问题发布的可行答案似乎都具有共同的 getline() 函数, POSIX getline()popen()等函数非常有用且强大,但遗憾的是 Windows 环境中不包含这些函数。

因此,在 Windows 环境中实现这样的任务需要不同的方法。 这里的链接描述了一种可以从stdin读取输入的方法,并且已经在开发它的系统上测试了高达 1.8 GB 的数据。 (也在链接中进行了描述。)_下面的简单代码片段使用以下命令行进行了测试,以在stdin上读取大量数据:

cd c:\dev && dir /s  // approximately 1.8Mbyte buffer is returned on my system 

简单的例子:

#include "cmd_rsp.h"
int main(void)
{
    char *buf = {0};
    buf = calloc(100, 1);//initialize buffer to some small value
    if(!buf)return 0;
    cmd_rsp("dir /s", &buf, 100);//recursive directory search on Windows system
    printf("%s", buf);
    free(buf);
    
    return 0;
}

cmd_rsp()在上面的链接中有完整的描述,但它本质上是一个 Windows 实现,包括popen()getline()类的功能,打包到这个非常简单的函数中。

如果你想输入未知长度的字符串或输入尝试使用以下代码。

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


int main()
{
char  *m;
clrscr();
printf("please input a string\n");
scanf("%ms",&m);
if (m == NULL)
    fprintf(stderr, "That string was too long!\n");
else
{
    printf("this is the string %s\n",m);
    /* ... any other use of m */
    free(m);


}

getch();
return 0;

}

请注意 %ms, %as 是 GNU 扩展..

暂无
暂无

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

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