c-在程序中间更改输入流



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

#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: %cn", (char) ch);
}

运行它:

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

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

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

我之前的建议不起作用,因为cat命令只会将文件和stdin连接为一个文件,并将其提供给程序,您最终会得出相同的结论。

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

#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 FILENAMEn", arg0);
fprintf(stderr, "n");
fprintf(stderr, "This program reads and outputs FILENAME, thenn");
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, "%s: %s.n", argv[1], 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]是有效的。在POSIXy系统中,如Linux、BSD和Mac,以及符合C11、argv[argc] == NULL的标准C库,并且可以安全访问。

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

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

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

    因此,if行读取"em";如果argc说我们在argv数组中没有正好两个元素,或者我们有和argv[1]匹配-h,或者我们和argv[2]匹配-help,那么"。

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

    arg0的值是三元表达式;如果argc说我们应该在argv数组中至少有一个元素,并且argv数组存在,而且argv数组的第一个元素存在,并且第一个元素中的第一个字符不是字符串结束标记,那么arg0是argv数组内的第一个元件;否则,arg0为(this)">

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

  • 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)用于错误消息和使用信息。

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

相关内容

  • 没有找到相关文章

最新更新