打开文件或标准输入时的错误处理

问题描述 投票:0回答:2

如果提供文件名,我的程序从文件中读取,否则从

stdin
读取:

if (isatty(STDIN_FILENO)) {
    if ( !(f = fopen(argv[1], "r")) )
    {
        perror(argv[1]);
        return 1;
    }
}
else
    f = stdin;

while ((size = getline(&line, &len, f)) != -1) {
...

这是正确的错误处理吗? 我是否需要对

stdin
进行错误处理,或者是否没有打开失败的情况?

顺便说一句,我有点困惑,

isatty(STDIN_FILENO)
在字面上“是NOT一个TTY”的地方返回真。

我本以为我的 if 检查会反向工作。

c file error-handling getline
2个回答
2
投票

你只需要

FILE *f;
if ( argc < 2 || strcmp( argv[1], "-" ) == 0 ) {
   f = stdin;
} else {
   f = fopen( argv[1], "r" );
   if ( !f ) {
      perror(argv[1]);
      exit( 1 );
   }
}

解释如下。


你把事情复杂化了。您只需要查看参数即可。

  • ./prog
    应该从
    stdin
    .
  • ./prog -
    应该从
    stdin
    读。 (可选)
  • ./prog file
    应该从文件中读取
    file
    .

如果是这样,

  • ./prog file
    按预期工作。
  • ./prog <file
    按预期工作。
  • cat file | ./prog
    按预期工作。

这是一个常见的范例。

$ echo 'From file' >file

$ echo 'From stdin' | cat
From stdin
$ echo 'From stdin' | cat -
From stdin
$ echo 'From stdin' | cat file
From file

$ echo 'From stdin' | grep ''
From stdin
$ echo 'From stdin' | grep '' -
From stdin
$ echo 'From stdin' | grep '' file
From file

$ echo 'From stdin' | perl -pe1
From stdin
$ echo 'From stdin' | perl -pe1 -
From stdin
$ echo 'From stdin' | perl -pe1 file
From file

$ echo 'echo "From file"' >file

$ echo 'echo "From stdin"' | sh
From stdin
$ echo 'echo "From stdin"' | sh -
From stdin
$ echo 'echo "From stdin"' | sh file
From file

根据它是否是终端来决定是否从 stdin 读取可能会导致问题。 stdin 不是终端是很常见的。例如,守护进程的标准输入没有连接到终端,因此由守护进程启动的程序的标准输入不是终端(除非守护进程采取措施使其成为终端)。

isatty
只应在此处使用,如果你想拒绝从终端读取。我见过一两个程序这样做,但这种情况极为罕见。


我有点困惑 isatty(STDIN_FILENO) 在字面上“不是 TTY”的地方返回 true。

你确实糊涂了。它恰恰相反。 “如果 fd 是指向终端的打开文件描述符,则 isatty() 返回 1;否则返回 0,并设置 errno 以指示错误。”


2
投票

如果提供文件名,我的程序从文件中读取,否则从

stdin
读取

您应该只测试是否提供了文件名并使用

fopen()
打开文件或将
stdin
分配给
FILE *f
否则。

读完

f
后,您还可以比较
f == stdin
来决定是关闭文件还是保持
stdin
打开。

isatty()
用于测试标准输入是否连接到终端,
STDIN_FILENO
是标准输入的系统句柄,
0
在 unix 系统上。这个函数不是 C 标准的一部分,而是在 POSIX 标准中定义的。

isatty(fd)
如果系统句柄
fd
指的是终端,则返回非零。如果
0
无效(并且
fd
设置为
errno
),并且如果
EBADF
不指代终端,则将
fd
设置为
errno
EINVAL
(由 POSIX.1-2001 规定)。
按照编码,程序测试标准输入是否已从文件或管道重定向,在这种情况下它从 

ENOTTY

读取,否则(如果

stdin
附加到终端)它尝试打开文件名。
stdin
当然会失败,如果没有提供文件名,将空指针传递给
fopen
实际上会导致未定义的行为。只是测试是否提供了文件名似乎更合适。
如果没有提供文件名或者文件名是

fopen

,在许多unix工具中习惯从

stdin
读取,所以你可能也想支持这个约定。
还要注意,

"-"

接受一个指向缓冲区分配大小的指针,并返回从流中读取的行的长度。因此,转置变量名称似乎更一致。

getline

© www.soinside.com 2019 - 2024. All rights reserved.