我有以下程序可以打印文件中的文本:
#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]
。因为argc
是argv
中的条目数,所以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
)用于错误消息和使用信息。如果用户运行程序不正确,或者发生错误,并且输出被重定向到一个文件或管道传输到另一个程序,他们通常仍然会看到标准的错误输出。(不过,也可以重定向标准错误。)