Какой лучший способ проверить, существует ли файл в C?

322

Есть ли лучший способ, чем просто открыть файл?

int exists(const char *fname)
{
    FILE *file;
    if ((file = fopen(fname, "r")))
    {
        fclose(file);
        return 1;
    }
    return 0;
}
  • 1
    Вы используете Windows?
  • 1
    Нет, это для * nix.
Показать ещё 9 комментариев
Теги:
cross-platform
filesystems

8 ответов

490
Лучший ответ

Найдите функцию access(), найденную в unistd.h. Вы можете заменить свою функцию на

if( access( fname, F_OK ) != -1 ) {
    // file exists
} else {
    // file doesn't exist
}

Вы также можете использовать R_OK, W_OK и X_OK вместо F_OK, чтобы проверить разрешение на чтение, разрешение на запись и разрешение на выполнение (соответственно), а не существование, и вы можете ИЛИ любой из их вместе (т.е. проверить как разрешение на чтение, так и запись с помощью R_OK|W_OK)

Обновление. Обратите внимание: в Windows вы не можете использовать W_OK для надежного тестирования для разрешения на запись, поскольку функция доступа не учитывает DACL. access( fname, W_OK ) может возвращать 0 (успех), потому что у файла нет атрибута только для чтения, но у вас все еще может не быть права на запись в файл.

  • 2
    Позвольте мне быть разборчивым :) access () не является стандартной функцией. Если «кроссплатформенность» следует понимать в более широком смысле, это может не сработать :)
  • 0
    Определите «стандартную функцию». Есть ли платформы там, где его нет?
Показать ещё 23 комментария
95

Используйте stat следующим образом:

int file_exist (char *filename)
{
  struct stat   buffer;   
  return (stat (filename, &buffer) == 0);
}

и назовите его следующим образом:

if (file_exist ("myfile.txt"))
{
  printf ("It exists\n");
}
  • 1
    Это не работает для файлов размером более 2 ГБ во всех системах.
  • 3
    @LudvigANorin: в таких системах есть вероятность, что у access() также есть проблемы, и есть опции, которые нужно использовать, чтобы access() и stat() работали с большими файлами (больше 2 ГБ).
Показать ещё 3 комментария
64

Обычно, когда вы хотите проверить, существует ли файл, это потому, что вы хотите создать этот файл, если это не так. Ответ Graeme Perrow хорош, если вы не хотите создать этот файл, но если вы это сделаете, он будет уязвим для условий гонки: другой процесс может создать файл между вами, проверяя, существует ли он, и вы фактически открывая его, чтобы написать ему. (Не смейтесь... это может иметь последствия плохой безопасности, если созданный файл был символической ссылкой!)

Если вы хотите проверить наличие и создать файл, если он не существует, atomically, чтобы не было условий гонки, используйте это:

#include <fcntl.h>
#include <errno.h>

fd = open(pathname, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
if (fd < 0) {
  /* failure */
  if (errno == EEXIST) {
    /* the file already existed */
    ...
  }
} else {
  /* now you can use the file */
}
  • 7
    Если вы собираетесь использовать O_CREAT, вам нужно предоставить режим (права доступа) в качестве третьего аргумента для open (). Также подумайте, следует ли использовать O_TRUNC, O_EXCL или O_APPEND.
  • 6
    Джонатан Леффлер прав, этот пример требует, чтобы O_EXCL работал как написано.
Показать ещё 2 комментария
29

Да. Используйте stat(). См. ссылка.

Stat не удастся, если файл не существует, иначе, скорее всего, удастся. Если он существует, но у вас нет доступа для чтения в каталог, в котором он существует, он также завершится неудачей, но в этом случае любой метод не сработает (как вы можете проверить содержимое каталога, который вы не видите в соответствии с правами доступа? Просто вы не можете).

О, как уже упоминалось, вы также можете использовать access(). Однако я предпочитаю stat(), как если бы файл существовал, он сразу же получит мне много полезной информации (когда он был последним обновлен, насколько он велик, владелец и/или группа, которым принадлежит файл, права доступа и т.д.),.

  • 5
    доступ предпочтителен, если вам нужно только знать, существует ли файл. Stat () может быть подслушано, если вам не нужна вся дополнительная информация.
  • 4
    На самом деле, когда я перечисляю каталог с помощью ls-команды, он вызывает stat для каждого файла, присутствующего там, и то, что запуск ls имеет большие накладные расходы, является довольно новым для меня. На самом деле вы можете запускать ls для каталогов с тысячами файлов, и он возвращается за доли секунды.
Показать ещё 3 комментария
5
FILE *file;
    if((file = fopen("sample.txt","r"))!=NULL)
        {
            // file exists
            fclose(file);
        }
    else
        {
            //File not found, no memory leak since 'file' == NULL
            //fclose(file) would cause an error
        }
  • 1
    Не приведет ли это к утечке памяти? Вы никогда не закрываете файл, если он существует.
  • 0
    Вы можете быть правы.
Показать ещё 7 комментариев
5

Из справки Visual С++, я бы пошел с

/* ACCESS.C: This example uses _access to check the
 * file named "ACCESS.C" to see if it exists and if
 * writing is allowed.
 */

#include  <io.h>
#include  <stdio.h>
#include  <stdlib.h>

void main( void )
{
   /* Check for existence */
   if( (_access( "ACCESS.C", 0 )) != -1 )
   {
      printf( "File ACCESS.C exists\n" );
      /* Check for write permission */
      if( (_access( "ACCESS.C", 2 )) != -1 )
         printf( "File ACCESS.C has write permission\n" );
   }
}

Также стоит отметить значения режима _accesss (const char * path, int mode)

00 Только существование

02 Разрешение на запись

04 Разрешение на чтение

06 Разрешение на чтение и запись

Как ваш fopen может выйти из строя в ситуациях, когда файл существует, но не может быть открыт по запросу.

Изменить: Просто прочитайте пост Мекки. stat() выглядит более аккуратным способом. Ho hum.

  • 0
    доступ предпочтителен, если вам нужно только знать, существует ли файл. Stat () может иметь большой подслушанный.
2

Я думаю, что функция access(), которая находится в unistd.h, является хорошим выбором для Linux (вы можете использовать stat).

Вы можете использовать его так:

#include <stdio.h>
#include <stdlib.h>
#include<unistd.h>

void fileCheck(const char *fileName);

int main (void) {
    char *fileName = "/etc/sudoers";

    fileCheck(fileName);
    return 0;
}

void fileCheck(const char *fileName){

    if(!access(fileName, F_OK )){
        printf("The File %s\t was Found\n",fileName);
    }else{
        printf("The File %s\t not Found\n",fileName);
    }

    if(!access(fileName, R_OK )){
        printf("The File %s\t can be read\n",fileName);
    }else{
        printf("The File %s\t cannot be read\n",fileName);
    }

    if(!access( fileName, W_OK )){
        printf("The File %s\t it can be Edited\n",fileName);
    }else{
        printf("The File %s\t it cannot be Edited\n",fileName);
    }

    if(!access( fileName, X_OK )){
        printf("The File %s\t is an Executable\n",fileName);
    }else{
        printf("The File %s\t is not an Executable\n",fileName);
    }
}

И вы получите следующий вывод:

The File /etc/sudoers    was Found
The File /etc/sudoers    cannot be read
The File /etc/sudoers    it cannot be Edited
The File /etc/sudoers    is not an Executable
0

Вы можете использовать функцию realpath().

resolved_file = realpath(file_path, NULL);
if (!resolved_keyfile) {
   /*File dosn't exists*/
   perror(keyfile);
   return -1;
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню