Programmation POSIX/Lecture et écriture de fichiers
Nous allons ici expliquer comment accéder au contenu des fichiers : comment ouvrir et fermer un fichier, lire et écrire dans un fichier, etc. Pour pouvoir manipuler le contenu d'un fichier, il faut utiliser un descripteur de fichier ouvert. Ce descripteur est un simple entier qui sera obtenu en utilisant la fonction d'ouverture de fichier et qui sera ensuite utilisé par toutes les autres fonctions manipulant les fichiers.
Ouverture
modifier#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open ( const char * chemin_fichier, int options );
int open ( const char * chemin_fichier, int options, mode_t mode );
int creat ( const char * chemin_fichier, mode_t mode );
L'appel système open convertit un chemin vers un fichier en descripteur de fichier ouvert. Ce qui revient suivant les options fournies à créer un nouveau fichier ou à en ouvrir un existant en lecture et/ou en écriture. Les valeurs possibles pour options sont des ou binaire entre O_RDONLY, O_WRONLY ou O_RDWR et une ou plusieurs des constantes symboliques suivantes :
- O_CREAT, créé le fichier s'il n'existe pas. Les droits du fichier sont ceux spécifiés dans l'argument mode ;
- O_EXCL, à utiliser en conjonction avec O_CREAT, provoque l'échec de l'appel si le fichier existe déjà ;
- O_TRUNC, si le fichier existe, est régulier et est ouvert en écriture, le fichier est tronqué à la longueur nulle (il est vidé) ;
- O_APPEND, le fichier est ouvert en mode ajout : la tête de lecture est placée à la fin du fichier.
(Il existe d'autres options que nous ne détaillerons pas ici)
L'argument mode défini les droits du fichier lorsque celui ci est créé par l'appel à open. Des constantes symboliques représentant les droits possibles sont définies, il est cependant beaucoup plus simple de les spécifier en utilisant une valeur en octal.
L'appel système creat est équivalent à open avec les options: O_CREAT | O_WRONLY | O_TRUNC.
Fermeture
modifier#include <unistd.h>
int close( int fd );
L'appel système close ferme le descripteur de fichier ouvert fd.
Exemple ouverture/fermeture
modifier#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
/* Entier qui servira à récupérer le descripteur de fichier */
int fd;
/* Appel à open, ouverture du fichier en lecture seule */
fd = open("/chemin/vers/mon/fichier", O_RDONLY);
if (fd == -1)
{
/* Quelque chose n'a pas fonctionné comme prévu, nous
affichons un message d'erreur et nous quittons le
programme.
*/
perror("open");
exit(EXIT_FAILURE);
}
/* Nous avons passé le test fd == -1, donc à partir d'ici
nous sommes sur que fd est bien un descripteur de fichier */
/* Nous fermons le fichier */
close(fd);
}
Lecture
modifier#include <sys/types.h>
#include <unistd.h>
ssize_t read (int fd, void * buffer, size_t nombre);
L'appel système read lit au plus nombre octets depuis le descripteur de fichier fd et les place dans le buffer. En cas d'erreur, read renvoie -1, sinon le nombre d'octets réellement lus est renvoyé. L'argument nombre peut être nul auquel cas rien n'est lu et read renvoie 0, par contre il doit être inférieur à SSIZE_MAX.
Remarque : Il est beaucoup plus intéressant, au niveau des performances, de lire de grandes quantités de données et de minimiser le nombre d'appels à read que de lire le fichier petits bouts par petits bouts (le pire étant de lire le fichier caractère par caractère).
Écriture
modifier#include <unistd.h>
ssize_t write (int fd, const void * buffer, size_t nombre);
L'appel système write écrit au plus nombre octets dans le descripteur de fichier ouvert fd. Le nombre d'octets réellement écrit est renvoyé si l'appel réussit, sinon write renvoie -1.
Lorsqu'un appel à read est effectué après un appel à write, les données venant d'être écrites sont renvoyées.
Exemple lecture/écriture
modifierLe programme suivant utilise tout ce que l'on a vu précédemment pour réaliser un programme permettant de copier un fichier (tel l'utilitaire Unix cp
). Le programme prend deux arguments : le fichier source et le fichier destination et recopie le premier dans le second en utilisant les fonctions read, write et un buffer de 100 octets (en fait de 100 caractères, en supposant que le type caractère correspond bien à un octet).
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char ** argv)
{
int source, destination;
char buffer[100];
int nb;
/* test des arguments du programme */
if (argc < 3)
{
fprintf(stderr, "usage: %s source destination\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Ouverture du fichier source en lecture seule */
source = open(argv[1], O_RDONLY);
if (source == -1)
{
perror("open fichier source");
exit(EXIT_FAILURE);
}
/* Ouverture du fichier destination en écriture (O_WRONLY)
si le fichier n'existe pas, on le créé (O_CREAT), s'il
existe, on le vide (O_TRUNC). */
destination = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (destination == -1)
{
perror("open fichier destination");
exit(EXIT_FAILURE);
}
/* Maintenant que nous avons nos deux fichiers ouverts,
nous effectuons la copie par bloc de 100 octets que
nous plaçons dans le buffer. Lorsque nous ne pouvons
plus lire, read renvoie 0 et nous sortons de la boucle. */
nb = read(source, buffer, 100);
while (nb > 0)
{
write(destination, buffer, nb);
nb = read(source, buffer, 100);
}
/* Fermeture des fichiers et fin du programme */
close(source);
close(destination);
return 0;
}
Déplacement
modifier#include <sys/types.h>
#include <unistd.h>
off_t lseek (int fd, off_t quantité, int départ);
L'appel système lseek permet de modifier la position courante dans un fichier en la déplaçant d'une certaine quantité à partir d'un point de départ. Ce point de départ peut être :
- SEEK_SET, le déplacement est effectué à partir du début du fichier ;
- SEEK_CUR, le déplacement est effectué à partir de la position courante ;
- SEEK_END, le déplacement est effectué à partir de la fin du fichier.
En particulier lseek(fd, 0, SEEK_SET)
permet de se positionner au début du fichier et lseek(fd, 0, SEEK_END)
à la fin.
La valeur renvoyé par lseek est le déplacement réellement effectué en nombre d'octets (ainsi en se positionnant au début puis à la fin du fichier, il est possible de calculer la taille de ce fichier).