Saltar a contenido

Ficheros

Descriptores de Ficheros

  • Unix IO
    fdesc.png
  • Descriptores estándar
    stdin.png

El puntero a un archivo: FILE *fptr;

  • El tipo de un archivo se considera texto o binario según la naturaleza de su contenido
  • fptr es un puntero a una estructura que define información sobre un archivo, incluyendo el nombre, el estado y la posición actual en el archivo. Es mantenido por el sistema y resulta clave para el correcto funcionamiento de las llamadas de E/S con buffer
  • El tipo FILE (descriptor de fichero) es un struct que se define en stdio.h como se muestra a continuación
    struct file {
      mode_t f_mode;
      loff_t f_pos;
      unsigned short f_flags;
      unsigned short f_count;
      unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
      struct file *f_next, *f_prev;
      int f_owner;         /* pid or -pgrp where SIGIO should be sent */
      struct inode * f_inode;
      struct file_operations * f_op;
      unsigned long f_version;
      void *private_data;  /* needed for tty driver, and maybe others */
    };
    

Resumen de llamadas al sistema para el manejo de ficheros

  • fopen(): Abre un archivo
  • fclose(): Cierra un archivo
  • fgets(): Lee un string de un archivo cuyo contenido es tipo texto
  • fputs(): Escribe string (a un archivo de texto)
  • fread()/fwite(): Lectura/Escritura binaria de/a un descriptor
  • ftell(), fseek(): Recorrer archivo
  • fprintf(): Escribe una salida con formato en el archivo
  • fscanf(): Lee una entrada con formato desde el archivo
  • feof(): Cierto si fin del archivo
  • ferror(): Cierto si se produce un error
  • rewind(): Se posiciona al principio del mismo
  • remove(): Borra archivo
  • fflush(): Vacía archivo

Operaciones básicas

1. Apertura de un archivo

fopen() obtiene un descriptor asociado al argumento nombre_archivo

FILE * fopen(const char *nombre_archivo, const char *modo);

nombre_archivo es un puntero a una cadena de caracteres que representan un nombre valido del archivo y puede incluir una especificación del directorio (path). La cadena a la que apunta modo determina cómo se abre el archivo. Los valores permitidos para modo son:
- r: Abre un archivo de texto para lectura
- w: Crea un archivo de texto para escritura
- a: Abre un archivo de texto para añadir
- rb: Abre un archivo en modo lectura binaria
- wb: Crea un archivo en modo escritura binaria
- ab: Abre un archivo binario para añadir datos
- r+: Abre un archivo de texto para lectura / escritura
- w+: Crea un archivo de texto para escritura / escritura
- a+: Añade o crea un archivo de texto para lectura / escritura
- r+b: Abre un archivo binario para lectura / escritura
- w+b: Crea un archivo binario para lectura / escritura
- a+b: Añade o crea un archivo binario para lectura / escritura

Siempre conviene tener presente lo siguiente:

  • fopen() devuelve un puntero a FILE. Un programa nunca debe alterar el valor de ese puntero
  • Si se produce un error cuando se esta intentando abrir un archivo, fopen() devuelve un puntero a NULL
  • El modo texto o binario tiene múltiples implicaciones. Por ejemplo, la relativa a la interpretación de los caracteres especiales como son el salto de línea (retorno de carro) o el fin de línea

2. Cierre de un archivo fclose()

fclose() cierra un descriptor abierto previamente mediante una llamada a fopen(). Escribe toda la información en el buffer al disco y realiza un cierre formal del archivo a nivel del sistema operativo. Un error en el cierre (el no cierre, por ejemplo) de una referencia puede generar todo tipo de problemas, incluyendo la pérdida de los datos, la destrucción de archivos y probables errores esotéricos en el programa. El prototipo de esta función es:

int fclose(FILE *f);

Si devuelve un valor cero significa éxito (curioso, pero cierto 🤷‍♀️)

Las llamadas a fopen() y fclose() deben ir siempre pareadas

Es decir, se deberán cerrar todos los ficheros abiertos antes de finalizar el programa

3. Leer y/o escribir en un archivo con fprintf() y fscanf()

int fprintf(FILE *f, const char *cadena_de_control, .....);  
int fscanf(FILE *f, const char *cadena_de_control, .....);

Como prinft() y scanf() excepto que operan sobre un descriptor de archivo (por ejemplo stdin, stdout)

4. Leer y/o escribir en un archivo de texto con fgets() y fputs()

Especialmente adecuadas para leer y escribir archivos de texto

  • man 3 fgets()
  • man 3 fputs()

char *fgets(char *str, int long, FILE *f);

OJO: si se alcanza el EOF (End of File) la llamada fgets() retorna NULL
char *fputs(char *str, FILE *f);

El siguiente código pone de manifiesto lo sencillo que resulta en C mostrar en pantalla un archivo de texto
char buff[1024]; // Buffer en memoria de 1024 caracteres  
FILE *fd;        // Descriptor de fichero
fd = fopen("/tmp/ejemplo.txt" , "r");            // Abrir en modo solo lectura
if(fd == NULL) {
      perror("Error al abrir /tmp/ejemplo.txt"); // Mostrar error en stderr
      return(-1);   // Devolver error genérico
 }  
// Leer máximo 1024 caracteres; podrían ser menos  
// buff funciona indistintamente como char[] y char *
if( fgets (buff, 1024, fd) != NULL )
      fputs(buff, stdout);  // imprime buffer en stdout
fclose(fp);      // OJO: cerrar siempre el descriptor para evitar efectos paranormales

5. Leer y/o escribir en un archivo binario con fread() y fwrite()

char *fread(void *ptr, size_t size, size_t n, FILE *f);

Ejemplo análogo al caso precedente pero con datos binarios (volcado del fichero)
char buff[1024]; // Buffer en memoria de 1024 caracteres  
FILE *fd;        // Descriptor de fichero   
int ret;  
fd = fopen("/tmp/ejemplo.txt" , "r"); // Abrir en modo solo lectura  
ret = fread(buff, 100, 1, fp);        // Leer 100 bytes
fputs(buff, stdout);                  // imprime buffer en stdout
fclose(fp);      // OJO: cerrar siempre el descriptor para evitar efectos paranormales

6. Fin de archivo feof()

int feof(FILE *f);

Cuando se abre un archivo para entrada binaria, la propia marca EOF se puede leer como un dato cualquiera. Esto podría hacer que la rutina de lectura indicase fin de archivo aún cuando el final físico no se haya alcanzado. Para resolver este problema, C incluye la función feof(), que determina cuando se ha alcanzado el real del archivo leyendo datos binarios
Su prototipo se encuentra en stdio.h. Devuelve cierto si se ha alcanzado el final del archivo, en cualquier otro caso, 0. Por supuesto, se puede aplicar igualmente a archivos de tipo texto

7. Recorrer archivo con fseek() y ftell()

Estas dos funciones son interesantes para cosas como, por ejemplo, leer un fichero al revés (hay, desde luego, formas mejores de hacer esto mismo)

int fseek(FILE *f, long int desplz, int posact);

Se desplaza el nº de bytes que indica desplaz desde la posición que indica posact. Esta puede valer SEEK_SET (inicio absoluto del fichero), SEEK_CUR (posición actual) o SEEK_END
Si no hubo error devuelve 0
long int ftell(FILE *f);

No tiene misterio alguno. Devuelve el valor actual del indicador de posición o -1 si error

8. Función ferror()

int ferror(FILE *f);

Su prototipo se encuentra en stdio.h. Determina si se ha producido error en una operación sobre un archivo. F es un puntero a un archivo válido. Devuelve cierto (1) si se ha producido un error en la última operación sobre el archivo. En caso contrario, devuelve falso (0). Debido a que cada operación sobre el archivo actualiza la condición de error, se debe llamar a ferror() inmediatamente después de la operación

9. Función remove()

int remove(char *nombre_archivo);

Borra el archivo especificado. Devuelve cero si tiene éxito

10. Función fflush()

int fflush(FILE *f);

Escribe todos los datos en el buffer en memoria al archivo en el disco
Si se llama esta función con un puntero nulo se vacían todos los buffers de todos los archivos abiertos. Devuelve cero si tiene éxito, en otro caso, devuelve EOF