【问题标题】:freopen() creates a file when given an invalid pathfreopen() 在给定路径无效时创建文件
【发布时间】:2020-07-09 21:27:46
【问题描述】:

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

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

在这种情况下,程序将只创建丢失的输出文件,而不是返回 NULL 并打印错误消息,这是不受欢迎的行为。怎么改,这样当找不到文件时,方法会返回NULL而不是新建文件?

【问题讨论】:

    标签: c file error-handling stdout freopen


    【解决方案1】:

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

    您可以使用open() 而不是使用freopen() 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;
    }
    

    【讨论】:

    • ... 假设 OP, open()。该函数由 POSIX 指定,不是基本语言标准的一部分。例如,Windows C 运行时库没有它。 (从技术上讲。Windows 有 _open(),相反,它在所有意图和目的下都使用不同的名称,但就 C 语言规范而言,它甚至不需要这样。)
    • OP 使用了$./dtoa ./tests/ExistingInput1.txt ./tests/NonExistingOutput.txt$ 提示符和正斜杠)所以open() 可能可用。
    • 可能是这样,但问题更多的是关于一般性,而不是关于 OP 是否可以使用所描述的方法。此外,以这种方式混合 I/O 套件是不雅的(但本质上不是错误的)。
    • @JohnBollinger 顺便问一下,如果不改变高级 I/O 操作的行为,像 dup2() 这样的系统调用的最初目的是什么?我们可以直接处理低级文件描述符,而不需要这种trick。我错过了什么吗?
    【解决方案2】:

    问题是当提供无效的输出文件路径时 [...] 程序只会创建丢失的输出文件而不是返回 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 的模式不允许写入,和/或如果您想确保即使没有写入目标文件也被截断,那么您可以重新打开它两次,首先在基于读取的模式下,如果成功,则在只写模式下:

        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;
            }
        }
    

    【讨论】:

      【解决方案3】:

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

      char path[256];
      scanf("%s", path);
      FILE* fp = NULL;
      
      fp = fopen(path, "r");
      if (!fp)
      {
          printf("The file doesen't exist!");
      }
      

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 2019-08-07
          • 2019-04-07
          • 1970-01-01
          • 2013-05-13
          • 2021-10-10
          • 1970-01-01
          • 2011-05-01
          • 2017-08-06
          相关资源
          最近更新 更多