Archivos en C – Linux

| 2013-08-23 | No hay comentarios »

En linux tenemos dos grupos de funciones para lectura y escritura de ficheros. Las funciones open()write()read() y close() son de algo más bajo nivel y específicas de linux. Dichas funciones serán tratadas en este artículo y dejaré ejemplos de cómo abrir y escribir archivos binarios. Los que desean utilizar funciones que son estándar de C, les recomiendo que lean este artículo

– Comencemos por crear un fichero. Existen dos maneras de abrir un fichero, open() y creat(). Antiguamente open() sólo podía abrir ficheros que ya estaban creados por lo que era necesario hacer una llamada a creat() para llamar a open() posteriormente. A día de hoy open() es capaz de crear ficheros, ya que se ha añadido un nuevo parámetro en su prototipo:

int creat( const char *pathname, mode_t mode )

int open( const char *pathname, int flags )

int open( const char *pathname, int flags, mode_t mode )

Por ello, para emplear estas syscalls se suelen incluir los ficheros de cabecera:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

El funcionamiento de open() es el siguiente: al ser llamada intenta abrir el fichero indicado en la cadena “pathname” con el acceso que indica el parámetro “flags”. Estos “flags” indican si queremos abrir el fichero para lectura, para escritura, etc. La siguiente tabla especifica los valores que puede tomar este parámetro:

Indicador Valor Descripción
O_RDONLY 0000 El fichero se abre sólo para lectura.
O_WRONLY 0001 El fichero se abre sólo para escritura.
O_RDWR 0002 El fichero se abre para lectura y escritura.
O_RANDOM 0010 El fichero se abre para ser accedido de forma aleatoria (típico de discos).
O_SEQUENTIAL 0020 El fichero se abre para ser accedido de forma secuencial (típico de cintas).
O_TEMPORARY 0040 El fichero es de carácter temporal.
O_CREAT 0100 El fichero deberá ser creado si no existía previamente.
O_EXCL 0200 Provoca que la llamada a open falle si se especifica la opción O_CREAT y el fichero ya existía.
O_NOCTTY 0400 Ei el fichero es un dispositivo de terminal (TTY), no se convertirá en la terminal de control de proceso (CTTY).
O_TRUNC 1000 Fija el tamaño del fichero a cero bytes.
O_APPEND 2000 El apuntador de escritura se situa al final del fichero, se escribirán al final los nuevos datos.
O_NONBLOCK 4000 La apertura del fichero será no bloqueante. Es equivalente a O_NDELAY.
O_SYNC 10000 Fuerza a que todas las escrituras en el fichero se terminen antes de que se retorne de la llamada al sistema. Es equivalente a O_FSYNC.
O_ASYNC 20000 Las escrituras en el fichero pueden realizarse de manera asíncrona.
O_DIRECT 40000 El acceso a disco se producirá de forma directa.
O_LARGEFILE 100000 Utilizado sólo para ficheros extremadamente grandes.
O_DIRECTORY 200000 El fichero debe ser un directorio.
O_NOFOLLOW 400000 Fuerza a no seguir los enlaces simbólicos. Útil en entornos críticos en cuanto a seguridad.

Tabla: Lista de los posibles valores del argumento “flags”.

La lista es bastante extensa y los valores están pensados para que sea posible concatenar o sumar varios de ellos, es decir, hacer una OR lógica entre los diferentes valores, consiguiendo el efecto que deseamos. Así pues, podemos ver que en realidad una llamada a creat() tiene su equivalente en open(), de esta forma:

open( pathname, O_CREAT | O_TRUNC | O_WRONLY, mode )

El argumento “mode” se encarga de definir los permisos dentro del Sistema de Ficheros (de la manera de la que lo hacíamos con el comando “chmod”). La lista completa de sus posibles valores es esta:

Indicador Valor Descripción
S_IROTH 0000 Activar el bit de lectura para todo los usuarios.
S_IWOTH 0001 Activar el bit de escritura para todo los usuarios.
S_IXOTH 0002 Activar el bit de ejecución para todo los usuarios.
S_IRGRP 0010 Activar el bit de lectura para todo los usuarios pertenecientes al grupo.
S_IRGRP 0020 Activar el bit de escritura para todo los usuarios pertenecientes al grupo.
S_IRGRP 0040 Activar el bit de ejecución para todo los usuarios pertenecientes al grupo.
S_IRUSR 0100 Activar el bit de lectura para el propietario.
S_IWUSR 0200 Activar el bit de escritura para el propietario.
S_IXUSR 0400 Activar el bit de ejecución para el propietario.
S_ISVTX 1000 Activa el “sticky bit” en el fichero.
S_ISGID 2000 Activa el bit de SUID en el fichero.
S_ISUID 4000 Activa el bit de SGID en el fichero.
S_IRWXU S_IRUSR + S_IWUSR + S_IXUSR Activar el bit de lectura, escritura y ejecución para el propietario.
S_IRWXG S_IRGRP + S_IWGRP + S_IXGRP Activar el bit de lectura, escritura y ejecución para todo los usuarios pertenecientes al grupo.
S_IRWXO S_IROTH + S_IWOTH + S_IXOTH Activar el bit de lectura, escritura y ejecución para todo los usuarios.

Tabla: Lista de los posibles valores del argumento “mode”.

Todos estos valores se definen en un fichero de cabecera , por lo que conviene incluirlo:

#include <sys/stat.h>

Bien, ya sabemos abrir ficheros y crearlos si no existieran, pero no podemos ir dejando ficheros abiertos sin cerrarlos convenientemente. Ya sabéis que C se caracteriza por tratar a sus programadores como personas responsables y no presupone ninguna niñera del estilo del recolector de basuras, o similares. Para cerrar un fichero basta con pasarle a la syscall close() el descriptor de fichero como argumento:

int close(int fd)

El siguiente paso lógico es poder leer y escribir en los ficheros que manejemos. Para ello emplearemos dos syscalls muy similares: read() y write(). Aquí tenemos sus prototipos:

ssize_t read( int fd, void *buf, size_t count )

ssize_t write( int fd, void *buf, size_t count )

Ejemplo para abrir archivo:


//Llamada a librerías
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//Función principal
main(int argc, char **argv[])
{
//Declaramos variables
int fd;
char c;
//ABRIR ARCHIVO
//O_RDONLY abre el archivo salida.txt en modo lectura
fd = open("salida.txt",O_RDONLY);

//CONTROLAR SI EXISTE ARCHIVO
if(fd!=-1)
{
//LEER EL ARCHIVO
//El archivo se lee caracter por caracter
while(read(fd,&c,sizeof(c)!=0)){
printf("%c",c);
}
//CERRAR ARCHIVO
close(fd);
}

else{
printf("\nEl archivo no existe");
}

}

Ejemplo para escribir en un archivo:


//Llamada a librerías
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//Función principal
main(int argc, char **argv[])
{
//Declaramos variables
int fd;
char *c;
//CREAR ARCHIVO
//O_WRONLY abre el archivo salida.txt en modo escritura
//O_CREAT crea el archivo si todavía no existe
//S_IRUSR|S_IWUSR son los modos que seleccione: Lectura y Escritura para el usuario
fd = open("salida3.txt",O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);
//fd = open("salida1.txt",O_WRONLY|00700);

//Ingresamos por teclado lo que queremos guardar en el archivo
scanf("%s",&c);

printf("el tamanho del string es %d\n",sizeof(c));

//Guardamos en el archivo lo que ingresamos por teclado
write(fd,&c,sizeof(c));

//CERRAR ARCHIVO
close(fd);

}

Ejemplo:  Abrir un archivo y copiar su contenido en otro:


//Llamada a librerías
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//Función principal
main(int argc, char **argv[])
{
//Declaramos variables
int fd,fd2;
char c;
//ABRIR ARCHIVO U ORIGEN
fd = open("salida.txt",O_RDONLY);
//CREAR ARCHIVO DE DESTINO
fd2 = open("destino.txt",O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);

//CONTROLAR SI EXISTE ARCHIVO
if(fd!=-1)
{

//LEER EL ARCHIVO
//El archivo se lee caracter por caracter
while(read(fd,&c,sizeof(c)!=0)){
//GUARDAR ARCHIVO NUEVO
write(fd2,&c,sizeof(c));
}

//CERRAR ARCHIVO
close(fd);
close(fd2);

fd2 = open("destino.txt",O_RDONLY);
//LEER EL ARCHIVO DESTINO PARA COMPROBAR SI TODO SALIO BIEN
//El archivo se lee caracter por caracter
while(read(fd2,&c,sizeof(c)!=0)){
printf("%c",c);
}
close(fd2);
}

else{
printf("\nEl archivo no existe");
}

}

Otra función que puede ser de gran ayuda es lseek(). Muchas veces no queremos posicionarnos al principio de un fichero para leer o para escribir, sino que lo que nos interesa es posicionarnos en un desplazamiento concreto relativo al comienzo del fichero, o al final del fichero, etc. La función lseek() nos proporciona esa posibilidad, y tiene el siguiente prototipo:

off_t lseek(int fildes, off_t offset, int whence);

Los parámetros que recibe son bien conocidos, “fildes” es el descriptor de fichero, “offset” es el desplazamiento en el que queremos posicionarnos, relativo a lo que indique “whence”, que puede tomar los siguientes valores:

Indicador Valor Descripción
SEEK_SET 0 Posiciona el puntero a “offset” bytes desde el comienzo del fichero.
SEEK_CUR 1 Posiciona el puntero a “offset” bytes desde la posición actual del puntero.
SEEK_END 2 Posiciona el puntero a “offset” bytes desde el final del fichero.

Tabla: Lista de los posibles valores del argumento “whence”.

Por ejemplo, si queremos leer un fichero y saltarnos una cabecera de 8 bytes, podríamos hacerlo así:


//Llamada a librerías
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define SIZE 512

//Función principal
main( int argc, char **argv[] )
{
 int fd, fd2, readbytes;
 //En este ejemplo utilizamos un buffer de 512 bytes
 char buffer[SIZE];

 //ABRIR ARCHIVO U ORIGEN
 fd = open("salida.txt",O_RDONLY);
 //CREAR ARCHIVO DE DESTINO
 fd2 = open("destino2.txt",O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR|S_IXUSR);

//SALTEAR LOS PRIMEROS 8 bytes.
 lseek(fd,8,SEEK_SET);

while( (readbytes = read( fd, buffer, SIZE )) != 0 )
 {
 /* write( STDOUT, buffer, SIZE ); */
 write( fd2, buffer, readbytes );
 }
 //CERRAR ARCHIVOS
 close( fd );
 close( fd2 );

}

Ya sabemos crear, abrir, cerrar, leer y escribir, ¡con esto se puede hacer de todo!

Acerca del autor: Rodrigo Paszniuk

Ingeniero Informático, amante de la tecnología, la música, el ciclismo y aprender cosas nuevas.

Posts Relacionados

  • Developers SO Sistemas Operativos preferidos por los developers
  • Instalar Tomcat 7 en CentOS 6
  • RPC (Remote Procedure Call) en C – Linux
  • Sockets en C (Parte II) – Linux



SEGUÍNOS EN FACEBOOK


GITHUB