简体   繁体   English

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

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

I am trying to read an unknown length line from stdin using the C language.我正在尝试使用 C 语言从 stdin 读取未知长度的行。

I have seen this when looking on the net:我在网上看的时候看到过这个:

char** str;
gets(&str);

But it seems to cause me some problems and I don't really understand how it is possible to do it this way.但这似乎给我带来了一些问题,我真的不明白如何以这种方式做到这一点。

Can you explain me why this example works/doesn't work and what will be the correct way to implement it (with malloc?)你能解释一下为什么这个例子有效/无效以及实现它的正确方法是什么(使用 malloc?)

You don't want a pointer to pointer to char , use an array of char s你不想要一个指向char指针的指针,使用一个char数组

char str[128];

or a pointer to char或指向char的指针

char *str;

if you choose a pointer you need to reserve space using malloc如果选择指针,则需要使用malloc保留空间

str = malloc(128);

Then you can use fgets然后你可以使用fgets

fgets(str, 128, stdin);

and remove the trailling newline并删除尾随的换行符

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

To read an arbitrary long line, you can use getline (a function added to the GNU version of libc):要读取任意长行,您可以使用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;
}

or your own implementation using fgets and realloc :或使用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;
}

Call it using调用它使用

char *str = getline(stdin);

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

More info 更多信息

Firstly, gets() provides no way of preventing a buffer overrun.首先, gets()没有提供防止缓冲区溢出的方法。 That makes it so dangerous it has been removed from the latest C standard.这使得它非常危险,它已从最新的 C 标准中删除。 It should not be used.不应该使用它。 However, the usual usage is something like但是,通常的用法类似于

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

Your usage is passing gets() a pointer to a pointer to a pointer to char.您的用法是将gets()传递给指向char 指针的指针。 That is not what gets() expects, so your code would not even compile.这不是gets()期望的,因此您的代码甚至无法编译。

That element of prayer reflected in the comment is why gets() is so dangerous.评论中反映的祈祷元素就是为什么gets()如此危险。 If the user enters 20 (or more) characters, gets() will happily write data past the end of buffer .如果用户输入 20 个(或更多)字符, gets()会很高兴地将数据写入buffer的末尾。 There is no way a programmer can prevent that in code (short of accessing hardware to electrocute the user who enters too much data, which is outside the realm of standard C).程序员无法在代码中阻止这种情况(除非访问硬件以电击输入过多数据的用户,这超出了标准 C 的范围)。

To answer your question, however, the only ways involve allocating a buffer of some size, reading data in some controlled way until that size is reached, reallocating if needed to get a greater size, and continuing until a newline (or end-of-file, or some other error condition on input) is encountered.但是,要回答您的问题,唯一的方法涉及分配某个大小的缓冲区,以某种受控方式读取数据直到达到该大小,如果需要重新分配以获得更大的大小,并继续直到换行(或结束)文件,或其他一些输入错误条件)。

malloc() may be used for the initial allocation. malloc()可用于初始分配。 malloc() or realloc() may be used for the reallocation (if needed). malloc()realloc()可用于重新分配(如果需要)。 Bear in mind that a buffer allocated this way must be released (using free() ) when the data is no longer needed - otherwise the result is a memory leak.请记住,当不再需要数据时,必须释放以这种方式分配的缓冲区(使用free() ) - 否则结果是内存泄漏。

use the getline() function, this will return the length of the line, and a pointer to the contents of the line in an allocated memory area.使用 getline() 函数,这将返回行的长度,以及指向已分配内存区域中该行内容的指针。 (be sure to pass the line pointer to free() when done with it ) (确保在完成后将行指针传递给 free() )

"Reading an unknown length line from stdin in c with fgets" “使用 fgets 从 c 中的 stdin 读取未知长度的行”

Late response - A Windows approach:延迟响应 - Windows 方法:

The OP does not specify Linux or Windows, but the viable answers posted in response for this question all seem to have the getline() function in common, which is POSIX only . OP 没有指定 Linux 或 Windows,但针对这个问题发布的可行答案似乎都具有共同的 getline() 函数, POSIX Functions such as getline() and popen() are very useful and powerful but sadly are not included in Windows environments. getline()popen()等函数非常有用且强大,但遗憾的是 Windows 环境中不包含这些函数。

Consequently, implementing such a task in a Windows environment requires a different approach.因此,在 Windows 环境中实现这样的任务需要不同的方法。 The link here describes a method that can read input from stdin and has been tested up to 1.8 gigabytes on the system it was developed on. 这里的链接描述了一种可以从stdin读取输入的方法,并且已经在开发它的系统上测试了高达 1.8 GB 的数据。 (Also described in the link.)_ The simple code snippet below was tested using the following command line to read large quantities on stdin : (也在链接中进行了描述。)_下面的简单代码片段使用以下命令行进行了测试,以在stdin上读取大量数据:

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

Simple example:简单的例子:

#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() is fully described in the links above, but it is essentially a Windows implementation that includes popen() and getline() like capabilities, packaged up into this very simple function. cmd_rsp()在上面的链接中有完整的描述,但它本质上是一个 Windows 实现,包括popen()getline()类的功能,打包到这个非常简单的函数中。

if u want to input an unknown length of string or input try using following code.如果你想输入未知长度的字符串或输入尝试使用以下代码。

#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;

}

Note that %ms, %as are GNU extensions..请注意 %ms, %as 是 GNU 扩展..

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

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