freopen () создает файл, когда ему указан неверный путь - PullRequest
0 голосов
/ 29 марта 2020

У меня есть программа, которая получает два пути в качестве аргументов командной строки. Первый аргумент (фактически второй, так как первый является самим именем команды) - это путь к файлу, из которого программа читает (входной файл). Второй - это путь к файлу, в который программа записывает (выходной файл).

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 вместо создания нового файла?

Ответы [ 4 ]

2 голосов
/ 29 марта 2020

Трюк с режимом "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;
}
1 голос
/ 29 марта 2020

Проблема заключается в том, что при неправильном пути к выходному файлу программа просто создаст отсутствующий выходной файл вместо возврата NULL

Это документированное поведение для открытия файла в режиме w (или любом другом режиме, основанном на w или a). Если это не то, что вы хотите - и вы должны подумать, так ли это на самом деле - тогда вам нужно использовать другой режим, по крайней мере, на начальном этапе. Все режимы 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;
        }
    }
1 голос
/ 29 марта 2020

Это документированное поведение. Если файл не существует, он создается.

0 голосов
/ 29 марта 2020

Попробуйте использовать fopen с режимом 'r' и убедитесь, что указатель файла равен NULL, чтобы вы могли узнать, существует файл или нет. Пример:

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

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