freopen() 在给定无效路径时创建文件

freopen() creates a file when given an invalid path

我有一个程序获取两个路径作为命令行参数。第一个参数(实际上是第二个,因为第一个是命令名称本身)是程序从中读取的文件(输入文件)的路径。第二个是程序写入的文件(输出文件)的路径。

int main(int argc, char *argv[])
{
    int num;
    /*if there are too many arguments print an error message*/
    if(argc > 3)
    {
        perror("too many arguments");
        return EXIT_FAILURE;
    }
    /*if there is at least one argument use it as path to input file*/
    if(argc > 1)
        if (!freopen(argv[1], "r", stdin)) {
            perror("Path of input file is Invalid");
            return EXIT_FAILURE;
        }
    /*if there are two arguments use the second one as output*/
    if(argc == 3)
        if (!freopen(argv[2], "w", stdout))
        {
            perror("Path of output file is Invalid");
            return EXIT_FAILURE;
        }
/*more code....*/
}
/*(compiled to run file "dtoa.out")*/

该程序工作正常:如果提供了有效的输入和路径输出路径,它将从文件中读取和写入,如果提供的参数太多或输入文件的路径无效,程序将打印一个错误信息并退出。

问题是提供了无效的输出文件路径:

$./dtoa ./tests/ExistingInput1.txt ./tests/NonExistingOutput.txt

在这种情况下,程序将只创建丢失的输出文件,而不是 returning NULL 并打印错误消息,这是不受欢迎的行为。我该如何更改它,以便在找不到文件时,该方法将 return NULL 而不是创建新文件?

这是记录在案的行为。如果文件不存在,则创建它。

尝试使用模式为'r'的fopen并检查文件指针是否为NULL,这样你就可以知道文件是否存在。 示例:

char path[256];
scanf("%s", path);
FILE* fp = NULL;

fp = fopen(path, "r");
if (!fp)
{
    printf("The file doesen't exist!");
}

"r+" 模式技巧(在问题的评论中给出)可以 足够了,但也使 stdout 可供阅读。
(这是个问题吗?)

而不是使用 freopen() 你可以使用 open() 而不是 O_CREAT 标志。
然后可以将得到的文件描述符代入标准输出 dup2().
http://man7.org/linux/man-pages/man2/open.2.html
http://man7.org/linux/man-pages/man2/dup.2.html

这是一个最小的例子。

$ rm -f output.txt
$ ./prog_c output.txt
before
after
$ touch output.txt
$ ./prog_c output.txt
before
$ cat output.txt 
after
$ 

源代码:


/**
  gcc -std=c99 -o prog_c prog_c.c \
      -pedantic -Wall -Wextra -Wconversion \
      -Wc++-compat -Wwrite-strings -Wold-style-definition -Wvla \
      -g -O0 -UNDEBUG -fsanitize=address,undefined
**/

// for printf()
#include<stdio.h>

// for open()
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if defined _MSC_VER // compiling with visual-studio
# include <io.h>
#endif

// for dup2() and close()
#include <unistd.h>

int
main(int argc,
     char **argv)
{
  printf("before\n");
  fflush(stdout); // in case something is pending in buffer...
  if(argc>1)
  {
#if defined _MSC_VER // compiling with visual-studio
    int fd=_open(argv[1], _O_WRONLY|_O_TRUNC); // no _O_CREAT
#else
    int fd=open(argv[1], O_WRONLY|O_TRUNC); // no O_CREAT
#endif
    if(fd!=-1)
    {
      dup2(fd, STDOUT_FILENO);
      close(fd);
    }
  }
  printf("after\n");
  return 0;
}

The problem is when provided with invalid output file path [...] the program will just create the missing output file instead of returning NULL

这是用 w 模式(或基于 wa 的任何其他模式)打开文件的记录行为。如果那不是您想要的——并且您应该考虑事实是否如此——那么您需要使用不同的模式,至少在最初是这样。如果文件不存在,所有 r 模式都会导致 fopen()freopen 失败,正如您所要求的那样。其中一些以允许读写的方式打开文件,例如,

    if (argc == 3) {
        if (!freopen(argv[2], "r+", stdout)) {
            perror("Path of output file is Invalid");
            return EXIT_FAILURE;
        }
    }

如果你想确保stdout的模式不允许写入,and/or如果你想确保目标文件即使没有写入也被截断,那么你可以重新打开它两次,首先以基于读取的模式,如果成功,则以只写模式:

    if (argc == 3) {
        if (!freopen(argv[2], "r+", stdout)) {
            perror("freopen (output)");
            return EXIT_FAILURE;
        }
        if (!freopen(NULL, "w", stdout)) {
            perror("freopen (output)");
            return EXIT_FAILURE;
        }
    }