微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在程序中更改输入流

如何解决在程序中更改输入流

我有以下程序可以打印文件中的文本:

#include<stdio.h>
int main(void) {
    int ch;

    while ((ch=getchar()) != EOF) {
        putchar(ch);
    }

    printf("Enter a character Now...\n");
    // possible to prompt input Now from a user?
    ch = getchar();
    printf("The character you entered was: %c\n",(char) ch);

}

并运行它:

$ ./io2 < file.txt
This is a text file
Do you like it?
Enter a character Now...
The character you entered was: �

在此之后,我将如何让用户输入字符。例如,如果我要执行 getchar()(不将文件重定向到标准输入时)?

现在看来,如果我在最后继续执行 EOF,它似乎只会继续打印 getchar() 字符。

解决方法

我之前的建议行不通,因为 cat 命令只会将文件和标准输入合并为一个并将其提供给您的程序,您最终会得出相同的结论。

如果你的程序需要这个文件,它应该直接读取它,然后从标准输入中获取其余的输入...

#include<stdio.h>
#include<stdlib.h>
int main(void) {
    int ch;
    FILE* file = fopen("file.txt","r");
    if (file == NULL) {
        perror("fopen");
        return EXIT_FAILURE;
    }

    while ((ch=fgetc(file)) != EOF) {
        putchar(ch);
    }

    fclose(file);
    ... // now just read from stdin

    return EXIT_SUCCESS;
}
,

您可以调用 clearerr(stdin) 来清除文件结束(和错误)条件:

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

int main(void)
{
    int  c;

    /* Consume standard input */
    while ((c = getchar()) != EOF) {
        putchar(c);
    }

    /* Clear the error condition */
    clearerr(stdin);

    printf("Please provide more input.\n");
    fflush(stdout);

    /* Consume more standard input */
    while ((c = getchar()) != EOF) {
        putchar(c);
    }

    return EXIT_SUCCESS;
}

然而,这是错误的方法。如果你运行 echo Hello | ./io2,程序将不会等待额外的输入,因为标准输入是由 echo 提供的,并且不再连接到终端。

正确的做法是使用命令行参数指定文件名,并使用单独的FILE句柄读取:

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

int main(int argc,char *argv[])
{
    FILE *in;
    int   c;

    if (argc != 2 || !strcmp(argv[1],"-h") || !strcmp(argv[1],"--help")) {
        const char *arg0 = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
        fprintf(stderr,"\n");
        fprintf(stderr,"Usage: %s [ -h | --help ]\n",arg0);
        fprintf(stderr,"       %s FILENAME\n","This program reads and outputs FILENAME,then\n");
        fprintf(stderr,"prompts and reads a line from standard input.\n");
        fprintf(stderr,"\n");
        return EXIT_FAILURE;
    }

    /* Open specified file. */
    in = fopen(argv[1],"r");
    if (!in) {
        fprintf(stderr,"%s: %s.\n",argv[1],strerror(errno));
        return EXIT_FAILURE;
    }

    /* Read and output file,character by character ("slow") */
    while ((c = getc(in)) != EOF) {
        putchar(c);
    }

    /* Check if the EOF indicated an error. */
    if (ferror(in)) {
        fprintf(stderr,"%s: Read error.\n",argv[1]);
        return EXIT_FAILURE;
    }

    /* Close the input file.  Be nice,and check for errors. */
    if (fclose(in)) {
        fprintf(stderr,strerror(errno));
        return EXIT_FAILURE;
    }

    /* Prompt for new input. */
    printf("Please input something.\n");
    fflush(stdout);

    while (1) {
        c = getchar();
        if (c == EOF || c == '\n' || c == '\r')
            break;
        putchar(c);
    }

    printf("All done.\n");
    return EXIT_SUCCESS;
}

这里有几点值得注意:

  • argv[0] 是命令本身(在您的示例中为 ./io2)。第一个命令行参数argv[1]。因为argcargv 中的条目数,所以argc == 1 表示没有参数argc == 2 表示有一个参数,依此类推。

    如果 argc == 2,则 argv[0]argv[1] 有效。在 Linux、BSD 和 Mac 等 POSIXy 系统以及符合 C11 的标准 C 库中,argv[argc] == NULL 可以安全访问。

  • if 行依赖于 C 逻辑规则。特别是,如果您有 expr1 || expr2,并且 expr1 为真,则永远不会评估 expr2

    这意味着如果 argc != 2,则根本不评估 strcmp() 检查。

    当且仅当 !strcmp(argv[1],"-h") 匹配 argv[1] 时,-h 为真。

    因此,if 行读取,“如果 argc 说我们在 argv 数组中没有恰好两个元素,或者我们有和 argv[1] 匹配 -h,或者我们有和 argv [2] 匹配 --help,then".

  • 在 POSIXy 系统中通常可以执行没有任何参数的程序,例如通过 execl("./io2",NULL,NULL) 或其他一些非标准技巧。这意味着argc 为零在技术上是可能的。在那种情况下,我们不知道这个程序是如何执行的。

    arg0 的值是一个三元表达式,它本质上是这样的,“如果 argc 说我们应该在 argv 数组中至少有一个元素,并且 argv 数组存在,并且 argv 数组中的第一个元素存在,并且第一个元素中的第一个字符不是字符串结束标记,则 arg0 是 argv 数组中的第一个元素;否则,arg0 是 (this)。"

    这只是因为我喜欢在以 -h--help 作为第一个参数运行时打印用法。 (POSIXy 系统中几乎所有的命令行程序都这样做。)用法说明了如何运行这个程序,为此,我想使用与运行这个程序相同的命令;因此arg0

  • getc()/getchar()/fgetc() 返回 EOF 时,表示流中没有更多输入。发生这种情况的原因可能是流结束,或者发生了读取错误。

    我喜欢仔细检查错误。有些人认为它“没用”,但对我来说,错误检查很重要。作为用户,我想知道 - 不,我需要知道我的存储介质是否产生错误。因此,ferror(in) 检查对我很重要。仅当访问流 in 时发生读/写错误(I/O 错误)时才为真(非零)。

    同样,fclose(in) 也有可能报告延迟错误。我不相信只读流有可能发生,但我们写入的流肯定有可能发生,因为 C 标准库缓冲流数据,并且当我们关闭流时可能会发生最终的底层写操作处理。甚至 man 3 fclose 手册页都明确表示这是可能的。

    有些程序员说检查 fclose() 错误没有用,因为它们太罕见了。对我来说,作为用户,它是。我希望我用来报告它们检测到的错误的程序,而不是假设“嗯,这太罕见了,我不会去检查或报告这些错误”*。

  • 默认情况下,标准输出 (stdout) 是行缓冲的,因此技术上不需要 fflush(stdout)。 (前面的 printf() 以换行符 \n 结尾,这意味着 printf() 应该导致标准输出被刷新。)

    刷新流意味着确保 C 库实际将其内部缓冲区写入输出文件或设备。在这里,我们肯定希望在开始等待输入之前确保用户看到提示。因此,虽然技术上不需要 fflush(stdout),但在这里它也提醒我们人类程序员,此时我们确实需要将 stdout 流刷新到实际的输出设备(终端)。

  • 将程序输出重定向到文件或通过管道作为另一个程序的输入通常很有用。因此,我喜欢将标准错误 (stderr) 用于错误消息和使用信息。

    如果用户错误地运行程序,或发生错误,将输出重定向到文件或通过管道传输到另一个程序,他们通常仍会看到标准错误输出。 (不过,也可以重定向标准错误。)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。