将输入从标准输入传递到函数时进行缓冲

发布于 2024-08-05 11:21:01 字数 608 浏览 5 评论 0原文

昨天询问过这个问题,但我仍然遇到问题。我用 C 编写了一个程序,其中有一个处理文件的函数,这些文件可以通过文件指针传递给它。

void process_my_file(FILE *fptr, ...) {
    /* do work */
}

我问如何从标准输入读取输入并将其传递给我的函数,建议我尝试使用 stdin 作为参数调用该函数:

my_process_file(stdin, ...);

这可行,但我真正想做的是 从 stdin 读取直到遇到 EOF,然后将所有输入一次性传递给函数。仅传递 stdin 作为参数的问题是,每次用户输入一行输入并按“enter”键时,程序都会过早地吐出相应的输出行。

我希望输入和输出能够完全分离,这样输出只有在用户说出 EOF(Control-d)后才会出现。

再次提前致谢。我是一个学习编程的新手,你的建议对我很有帮助。我真的很欣赏这个网站。

——拉里

I asked about this yesterday, but I'm still having problems. I wrote a program in C which has a function that processes files which can be passed to it by a file pointer.

void process_my_file(FILE *fptr, ...) {
    /* do work */
}

I asked how to read input from standard input and pass it to my function, and it was recommended to me that I try calling the function with stdin as the argument:

my_process_file(stdin, ...);

This works, but what I'm really looking to do is read from stdin until EOF is encountered, and then pass all the input at once to the function. The issue with just passing stdin as the argument is that every time the user enters a line of input and presses "enter" the program spits out a corresponding line of output prematurely.

I was hoping for a clean separation of input and output, such that output only comes out after the user has said EOF (Control-d).

Thanks again in advance. I'm a newbie learning to program, and your tips are a big help. I really appreciate this website.

-- Larry

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

饭团 2024-08-12 11:21:01

假设您要打开一个文件,然后将文件句柄传递给您的函数。函数中的代码仍然必须读取该常规文件的 EOF。此外,它还必须分配足够的空间来存储文件,并处理短读取。

所有这些都与您必须处理 stdin 的问题相同 - 唯一可能的区别是,来自终端的 stdin 将为您提供每行输入的简短读取,而每次从管道读取都会为您提供简短的读取。读取管道缓冲区的大小(或小于缓冲区大小的原子写入),并且普通磁盘文件通常只会为您提供对文件最后一个块的简短读取。由于您的函数无法提前告知需要多少空间(当然不是管道或终端输入),因此您必须准备好处理动态内存分配 - malloc()realloc()。

另外,如果您的函数期望获取已为其读取的数据,为什么要传递文件句柄(FILE 指针)而不是字符缓冲区及其长度?当您需要函数使用文件句柄时,您可以将文件句柄传递给函数 - 从可读句柄读取数据,或写入可写句柄(并且,偶尔,如果句柄打开以进行读写)。


这是一个工作示例程序。我必须找出一些需要将整个文件放入内存中、处理它并输出一些答案的东西 - 所以我选择按字符对文件进行排序。毫无意义,但它演示了该怎么做。它还具有操作变量参数错误报告功能。

玩得开心!

/*
 * Demo code for StackOverflow question 1484693
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>

static char *arg0;

static void error(const char *fmt, ...)
{
    va_list args;
    int errnum = errno;  /* Catch errno before it changes */

    fprintf(stderr, "%s: ", arg0);
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    if (errnum != 0)
        fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
    fputc('\n', stderr);
    exit(1);
}

static int char_compare(const void *v1, const void *v2)
{
    char c1 = *(const char *)v1;
    char c2 = *(const char *)v2;
    if (c1 < c2)
        return -1;
    else if (c1 > c2)
        return +1;
    else
        return 0;
}

static void process_my_file(FILE *fp)
{
    char   *buffer;
    size_t  buflen = 1024;
    size_t  in_use = 0;
    ssize_t nbytes;

    if ((buffer = malloc(buflen)) == 0)
        error("out of memory - malloc()");

    while ((nbytes = fread(buffer + in_use, sizeof(char), buflen - in_use, fp)) > 0)
    {
        if (nbytes < 0)
            error("error from fread()");
        in_use += nbytes;
        if (in_use >= buflen)
        {
            char *newbuf;
            buflen += 1024;
            if ((newbuf = realloc(buffer, buflen)) == 0)
                error("out of memory - realloc()");
            buffer = newbuf;
        }
    }

    /* Consistency - number/size vs size/number! */
    qsort(buffer, in_use, sizeof(char), char_compare);
    fwrite(buffer, sizeof(char), in_use, stdout);
    putchar('\n');

    free(buffer);
}

int main(int argc, char **argv)
{
    arg0 = argv[0];

    if (argc > 1)
    {
        for (int i = 1; i < argc; i++)
        {
            FILE *fp;
            if ((fp = fopen(argv[i], "r")) == 0)
                error("failed to open file %s", argv[i]);
            process_my_file(fp);
            fclose(fp);
        }
    }
    else
        process_my_file(stdin);
    return(0);
}

您可以使用一个或多个文件名作为参数来调用它;每个文件名单独排序。你可以将一些东西输入其中;你可以让它从标准输入读取。
我选择忽略 fwrite()fclose() 可能失败的可能性;我还选择忽略 process_my_file() 中的 buflen 溢出的可能性。如果您选择,您可以检查它们。 (请注意,每个文件的输出比输入多包含一个换行符。)

读者练习:

  • 将不可打印的字符打印为 ''\xXX`' 转义序列。
  • 将输出分成每行不超过 64 个字符的行。
  • 设计或研究替代分配策略,例如将每次分配的空间加倍(请参阅“编程实践')

Suppose you were to open a file and then pass the file handle to your function. Your code in the function would still have to read to EOF on that regular file. Further, it would have to deal with allocating enough space to store the file, and deal with short reads.

All this is just the same set of issues that you must deal with for stdin - the only possible difference being that stdin coming from a terminal will give you short reads for each line of input, whereas each read from a pipe will give you a short read for the size of the pipe buffer (or the atomic writes smaller than the buffer size), and a plain disk file will only usually give you a short read on the last block of a file. Since your function cannot tell ahead of time how much space is needed (certainly not for the pipe or terminal inputs), you have to be prepared to deal with dynamic memory allocation - malloc() and realloc().

Also, if your function is expecting to get the data already read for it, why is it being passed a file handle (FILE pointer) and not a character buffer and its length? You pass a file handle to a function when you need the function to use it - to read from a readable handle, or to write to a writable handle (and, just occasionally, both if the handle is open for reading and writing).


Here's a working example program. I had to work out something that needed to slurp the whole file into memory, process it, and spew out some answer - so I've chosen to sort the file by characters. Moderately pointless, but it demonstrates what to do. It also has an operational variable arguments error reporting function in it.

Have fun!

/*
 * Demo code for StackOverflow question 1484693
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>

static char *arg0;

static void error(const char *fmt, ...)
{
    va_list args;
    int errnum = errno;  /* Catch errno before it changes */

    fprintf(stderr, "%s: ", arg0);
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    if (errnum != 0)
        fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
    fputc('\n', stderr);
    exit(1);
}

static int char_compare(const void *v1, const void *v2)
{
    char c1 = *(const char *)v1;
    char c2 = *(const char *)v2;
    if (c1 < c2)
        return -1;
    else if (c1 > c2)
        return +1;
    else
        return 0;
}

static void process_my_file(FILE *fp)
{
    char   *buffer;
    size_t  buflen = 1024;
    size_t  in_use = 0;
    ssize_t nbytes;

    if ((buffer = malloc(buflen)) == 0)
        error("out of memory - malloc()");

    while ((nbytes = fread(buffer + in_use, sizeof(char), buflen - in_use, fp)) > 0)
    {
        if (nbytes < 0)
            error("error from fread()");
        in_use += nbytes;
        if (in_use >= buflen)
        {
            char *newbuf;
            buflen += 1024;
            if ((newbuf = realloc(buffer, buflen)) == 0)
                error("out of memory - realloc()");
            buffer = newbuf;
        }
    }

    /* Consistency - number/size vs size/number! */
    qsort(buffer, in_use, sizeof(char), char_compare);
    fwrite(buffer, sizeof(char), in_use, stdout);
    putchar('\n');

    free(buffer);
}

int main(int argc, char **argv)
{
    arg0 = argv[0];

    if (argc > 1)
    {
        for (int i = 1; i < argc; i++)
        {
            FILE *fp;
            if ((fp = fopen(argv[i], "r")) == 0)
                error("failed to open file %s", argv[i]);
            process_my_file(fp);
            fclose(fp);
        }
    }
    else
        process_my_file(stdin);
    return(0);
}

You can call this with one or more file names as arguments; each file name is sorted separately. You can pipe something into it; you can let it read from standard input.
I choose to ignore the possibility that fwrite() and fclose() might fail; I also choose to ignore the possibility of overflow on buflen in process_my_file(). You can check them if you choose. (Note that the output for each file contains one more newline than the input does.)

Exercises for the reader:

  • Print non-printable characters as ''\xXX`' escape sequences.
  • Break the output into lines of not more than 64 characters each.
  • Devise or research alternative allocation strategies, such as doubling the space on each allocation (see 'The Practice of Programming')
靑春怀旧 2024-08-12 11:21:01

您必须自己进行预缓冲,即读取 stdin 直到看到 EOF,然后将一个长字符串(可能由 \n 分隔的行组成)传递给您的函数。或者您的预缓冲区读取例程可以分配指向分配行的 char* 数组。或者您的预缓冲例程将解析标准输入并返回预处理信息。取决于您想如何处理该信息。

You'll have to do the pre-buffering yourself, i.e., read stdin until EOF is seen and then pass one long string (probably constisting of \n-separated lines) to your function. Or your pre-buffer read routine could allocate an array of char*'s that point to allocated lines. Or your pre-buffer routine would parse stdin and return preprocessed info. Depends on what you want to do with the information.

天涯离梦残月幽梦 2024-08-12 11:21:01

您现在拥有的是“过滤器”。过滤器是很棒的程序,但当然并不适用于所有情况。不管怎样,看看你是否可以让你的程序作为过滤器工作。

如果您确实必须在处理之前读取所有输入,则必须将输入保存在某处,并且使用 FILE* 调用处理函数是没有意义的(FILE* 中的所有数据已经已读过);您可以将所有输入读取到字符数组中并将该数组传递给您的函数。

void process_data(char data[], size_t data_len) { /* do work */ }

What you have now is a 'filter'. Filters are wonderful programs but, of course, not applicable to all situations. Anyway, see if you can keep your program working as a filter.

If you really must read all the input before processing, you have to save the input somewhere, and, it makes no sense calling the processing function with a FILE* (all the data in the FILE* has already been read); you can read all the input into a char array and pass that array to your function.

void process_data(char data[], size_t data_len) { /* do work */ }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文