¿Cuál es la mejor manera de verificar si existe un archivo en C?

12 minutos de lectura

avatar de usuario
dave marshall

¿Hay una mejor manera que simplemente intentar abrir el archivo?

int exists(const char *fname)
{
    FILE *file;
    if ((file = fopen(fname, "r")))
    {
        fclose(file);
        return 1;
    }
    return 0;
}

  • Vos si De Verdad ¿Solo quieres comprobar la existencia? ¿O desea verificar y escribir en el archivo si aún no existe? Si es así, vea mi respuesta a continuación, para una versión que no sufre condiciones de carrera.

    – Dan Lensky

    23 de octubre de 2008 a las 17:14

  • No veo, ¿qué hay de malo en esa forma de abrir/cerrar?

    – Johannes Schaub – litb

    3 de febrero de 2009 a las 18:16

  • @JohannesSchaub-litb: una cosa que está mal con el fopen()/fclose() método es que es posible que no pueda abrir un archivo para leerlo aunque exista. Por ejemplo, /dev/kmem existe, pero la mayoría de los procesos no pueden abrirlo ni siquiera para leerlo. /etc/shadow es otro archivo de este tipo. Por supuesto, ambos stat() y access() confiar en poder acceder al directorio que contiene el archivo; todas las apuestas están canceladas si no puede hacer eso (sin permiso de ejecución en el directorio que contiene el archivo).

    –Jonathan Leffler

    18 de marzo de 2013 a las 6:23

  • if (file = fopen(fname, "r")) dará una advertencia. Use paréntesis alrededor de la declaración dentro de la declaración if if ((file = fopen(fname, "r")))

    – Joaquín

    13 de febrero de 2016 a las 11:47

  • @joakim (()) es resolver los síntomas, no el problema. Simplemente sepárelo en dos líneas; una línea extra no dolerá tanto. file = fopen(fname, "r"); if (file)

    – alx

    8 de julio de 2019 a las 20:12


avatar de usuario
Graeme Perrow

busca el access() función, que se encuentra en unistd.h. Puede reemplazar su función con

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

Bajo Windows (VC) unistd.h no existe. Para que funcione es necesario definir:

#ifdef WIN32
#include <io.h>
#define F_OK 0
#define access _access
#endif

También puedes usar R_OK, W_OKy X_OK en lugar de F_OK para verificar el permiso de lectura, el permiso de escritura y el permiso de ejecución (respectivamente) en lugar de la existencia, y puede O cualquiera de ellos juntos (es decir, verificar ambos lectura y permiso de escritura usando R_OK|W_OK)

Actualizar: Tenga en cuenta que en Windows, no puede usar W_OK para probar de forma fiable el permiso de escritura, ya que la función de acceso no tiene en cuenta las DACL. access( fname, W_OK ) puede devolver 0 (éxito) porque el archivo no tiene establecido el atributo de solo lectura, pero es posible que aún no tenga permiso para escribir en el archivo.

  • Permítanme ser exigente 🙂 access() no es una función estándar. Si se debe tomar “plataforma cruzada” en el sentido más amplio, esto puede fallar 🙂

    – Remo.D

    23 de octubre de 2008 a las 19:10

  • POSIX es un estándar ISO; define el acceso(). C es otra norma ISO; no es asi.

    –Jonathan Leffler

    24 de octubre de 2008 a las 7:23

  • Hay trampas asociadas con el acceso(). Hay una ventana de vulnerabilidad TOCTOU (hora de verificación, hora de uso) entre el uso de access() y cualquier otra cosa que haga después. […to be continued…]

    –Jonathan Leffler

    24 de octubre de 2008 a las 7:25

  • […continuing…] Más esotéricamente, en los sistemas POSIX, access() verifica si el UID real y el GID real, en lugar del UID efectivo y el GID efectivo. Esto solo importa para los programas setuid o setgid, pero luego importa mucho ya que puede dar una respuesta ‘incorrecta’.

    –Jonathan Leffler

    24 de octubre de 2008 a las 7:26

  • La mayoría de las veces, sí (está bien usar access() para verificar la existencia de un archivo), pero en un programa SUID o SGID, incluso eso podría ser incorrecto. Si el archivo probado está en un directorio al que no puede acceder el UID real o el GID real, access() podría informar que no existe tal archivo cuando existe. ¿Esotérico e improbable? Sí.

    –Jonathan Leffler

    18 de abril de 2013 a las 13:14


avatar de usuario
código conejito

Usar stat Me gusta esto:

#include <sys/stat.h>   // stat
#include <stdbool.h>    // bool type

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

y llámalo así:

#include <stdio.h>      // printf

int main(int ac, char **av) {
    if (ac != 2)
        return 1;

    if (file_exists(av[1]))
        printf("%s exists\n", av[1]);
    else
        printf("%s does not exist\n", av[1]);

    return 0;
}

  • @LudvigANorin: en tales sistemas, lo más probable es que access() también tiene problemas, y hay opciones para usar para hacer access() y stat() trabajar con archivos de gran tamaño (más de 2 GB).

    –Jonathan Leffler

    18 de marzo de 2013 a las 6:26

  • ¿Alguno de ustedes podría señalar la documentación sobre la falla después de 2 GB? Además, ¿cuál es la alternativa en tales casos?

    – chamakits

    23 de abril de 2013 a las 5:37

  • @JonathanLeffler lo hace stat no sufrir la misma vulnerabilidad TOCTOU que access? (No me queda claro que sería mejor).

    – Telémaco

    1 mayo 2014 a las 22:59

  • Ambas cosas stat() y access() sufre de la vulnerabilidad TOCTOU (también lstat()pero fstat() es seguro). Depende de lo que vaya a hacer en función de la presencia o ausencia del archivo. Usar las opciones correctas para open() suele ser la mejor manera de abordar los problemas, pero puede ser complicado formular las opciones correctas. Véanse también los debates sobre EAFP (más fácil pedir perdón que permiso) y LBYL (mirar antes de dar el salto); ver LBYL frente a EAFP en Java, por ejemplo.

    –Jonathan Leffler

    1 mayo 2014 a las 23:50

  • @Telemachus si necesita evitar TOCTOU, al menos en los sistemas Linux puede simplemente open() el archivo en TOC (la open() el resultado se convierte en la verificación de la existencia del archivo), luego use este descriptor en TOU. De esta manera, incluso si el archivo ya no existe en TOU, aún puede acceder a él a través del descriptor de archivo. Su contenido se mantendrá mientras existan procesos que lo tengan abierto.

    – Hola angel

    11 de enero de 2021 a las 9:38

avatar de usuario
mesutpiskin

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
        }

  • fopen() es estándar C, no va a ninguna parte. Solo está “obsoleto” por Microsoft. no usar fopen_s() a menos que desee un código no portátil específico de la plataforma.

    –Andrew Henle

    1 de marzo de 2017 a las 9:54

  • Este es el más compatible para todos los sistemas antiguos y nuevos.

    – remolino

    14 de julio de 2017 a las 14:09

  • ¿Dónde está el conjunto booleano?

    – Jean-François Fabre

    19 de septiembre de 2019 a las 13:58

avatar de usuario
michi

pienso que acceso() función que se encuentra en unistd.h es una buena opción para Linux (puedes usar estadística también).

Puedes usarlo así:

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

Y obtienes la siguiente salida:

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

avatar de usuario
Mecki

Sí. Usar stat(). Vea la página del manual parastat(2).

stat() fallará si el archivo no existe; de ​​lo contrario, lo más probable es que tenga éxito. Si existe, pero no tiene acceso de lectura al directorio donde existe, también fallará, pero en ese caso fallará cualquier método (¿cómo puede inspeccionar el contenido de un directorio que no puede ver de acuerdo con los derechos de acceso? Simplemente, no puedes).

Oh, como alguien más mencionó, también puedes usar access(). Sin embargo, prefiero stat()como si el archivo existiera, inmediatamente obtendría mucha información útil (cuándo se actualizó por última vez, qué tan grande es, propietario y/o grupo propietario del archivo, permisos de acceso, etc.).

  • se prefiere el acceso si solo necesita saber si el archivo existe. Stat() puede tener una gran cantidad de información si no necesita toda la información adicional.

    – Martín Beckett

    23 de octubre de 2008 a las 15:16

  • En realidad, cuando enumero un directorio usando ls-command, llama a stat para cada archivo que está presente allí y que ejecutar ls tiene una gran sobrecarga es bastante nuevo para mí. En realidad, puede ejecutar ls en directorios con miles de archivos y regresa en una fracción de segundo.

    – Mecki

    23 de octubre de 2008 a las 17:39

  • @Mecki: stat tiene una sobrecarga adicional distinta de cero en comparación con el acceso en sistemas que admiten enlaces duros. Esto se debe a que el acceso solo tiene que mirar la entrada del directorio, mientras que stat también tiene que buscar el inodo. En dispositivos de almacenamiento con mal tiempo de búsqueda (por ejemplo, cinta), la diferencia puede ser significativa ya que es poco probable que la entrada del directorio y el inodo estén uno al lado del otro.

    –Kevin

    24 de marzo de 2016 a las 1:42

  • @Kevin A menos que solo le pases F_OK, access() verifica los permisos de acceso a archivos de un archivo y estos se almacenan en el inodo para ese archivo y no están en su entrada de directorio (al menos para todos los sistemas de archivos que tienen estructuras similares a inodos). Asi que access() tiene que acceder al inodo exactamente de la misma manera que stat() tiene que acceder a ella. Entonces, ¡lo que dices solo es cierto si no verificas ningún permiso! Y de hecho en algunos sistemas access() incluso se implementa encima de stat() (por ejemplo, glibc en GNU Hurd lo hace de esa manera), por lo que no hay garantía en primer lugar.

    – Mecki

    01/04/2016 a las 13:33

  • @Mecki: ¿Quién dijo cualquier cosa acerca de comprobar los permisos? Estaba hablando específicamente de F_OK. Y sí, algunos sistemas están mal implementados. El acceso será al menos tan rápido como stat en todos los casos y puede ser más rápido algunas veces.

    –Kevin

    1 de abril de 2016 a las 15:17

avatar de usuario
grapas gabriel

Por lo general, cuando desea verificar si existe un archivo, es porque desea crear ese archivo si no es así. La respuesta de Graeme Perrow es buena si no desea crear ese archivo, pero es vulnerable a una condición de carrera si lo hace: otro proceso podría crear el archivo entre usted, verificando si existe, y realmente abriéndolo para escribir en él. (No te rías… esto podría haber malo implicaciones de seguridad si el archivo creado fuera un enlace simbólico!)

Si desea comprobar la existencia y crear el archivo si no existe, atómicamente para que no haya condiciones de carrera, entonces usa esto:

#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 */
}

  • se prefiere el acceso si solo necesita saber si el archivo existe. Stat() puede tener una gran cantidad de información si no necesita toda la información adicional.

    – Martín Beckett

    23 de octubre de 2008 a las 15:16

  • En realidad, cuando enumero un directorio usando ls-command, llama a stat para cada archivo que está presente allí y que ejecutar ls tiene una gran sobrecarga es bastante nuevo para mí. En realidad, puede ejecutar ls en directorios con miles de archivos y regresa en una fracción de segundo.

    – Mecki

    23 de octubre de 2008 a las 17:39

  • @Mecki: stat tiene una sobrecarga adicional distinta de cero en comparación con el acceso en sistemas que admiten enlaces duros. Esto se debe a que el acceso solo tiene que mirar la entrada del directorio, mientras que stat también tiene que buscar el inodo. En dispositivos de almacenamiento con mal tiempo de búsqueda (por ejemplo, cinta), la diferencia puede ser significativa ya que es poco probable que la entrada del directorio y el inodo estén uno al lado del otro.

    –Kevin

    24 de marzo de 2016 a las 1:42

  • @Kevin A menos que solo le pases F_OK, access() verifica los permisos de acceso a archivos de un archivo y estos se almacenan en el inodo para ese archivo y no están en su entrada de directorio (al menos para todos los sistemas de archivos que tienen estructuras similares a inodos). Asi que access() tiene que acceder al inodo exactamente de la misma manera que stat() tiene que acceder a ella. Entonces, ¡lo que dices solo es cierto si no verificas ningún permiso! Y de hecho en algunos sistemas access() incluso se implementa encima de stat() (por ejemplo, glibc en GNU Hurd lo hace de esa manera), por lo que no hay garantía en primer lugar.

    – Mecki

    01/04/2016 a las 13:33

  • @Mecki: ¿Quién dijo cualquier cosa acerca de comprobar los permisos? Estaba hablando específicamente de F_OK. Y sí, algunos sistemas están mal implementados. El acceso será al menos tan rápido como stat en todos los casos y puede ser más rápido algunas veces.

    –Kevin

    1 de abril de 2016 a las 15:17

avatar de usuario
bharat reddy

Puede usar la función realpath().

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

¿Ha sido útil esta solución?