【问题标题】:buffering when passing input from standard input to a function将输入从标准输入传递到函数时进行缓冲
【发布时间】:2023-03-22 07:57:01
【问题描述】:

我问过这个yesterday,但我仍然遇到问题。我用 C 语言编写了一个程序,它有一个处理文件的函数,该函数可以通过文件指针传递给它。

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

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

my_process_file(stdin, ...);

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

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

再次提前致谢。我是一个学习编程的新手,你的提示很有帮助。我非常感谢这个网站。

-- 拉里

【问题讨论】:

    标签: c function arguments buffering


    【解决方案1】:

    假设您要打开一个文件,然后将文件句柄传递给您的函数。您在函数中的代码仍然必须读取该常规文件上的 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 个字符的行。
    • 设计或研究替代分配策略,例如将每次分配的空间加倍(请参阅“The Practice of Programming”)

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

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

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

        【讨论】:

        • 也传递长度 - 以避免缓冲区溢出并处理数据中嵌入的空值 ('\0')。
        • 如果数据“允许”嵌入 NUL,我会使用 unsigned char data[] 而不是(普通)char data[]。长度总是很好。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-07
        • 1970-01-01
        • 1970-01-01
        • 2020-08-18
        相关资源
        最近更新 更多