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!