Gestion de base d'un fichier par descripteur de fichier
Remarque
- La gestion des fichiers par descripteur de fichier est la version officielle dans le monde Unix, cette méthode est donc portable sur toutes les versions Unix !
- Il est à noter que trois descripteurs sont réservés au processus courant. Les descripteurs 0, 1 et 2 respectivement attribués à l’entrée standard, la sortie standard et la sortie en erreur standard. Un mécanisme permet de les utiliser par duplication de descripteur.
- Le système attribut un numéro de descripteur en recherchant le premier descripteur libre dans sa liste. Il est donc tout a fait possible par exemple de réutiliser le descripteur 0. Pour cela il suffit de fermer ce descripteur puis de le réouvrir (voir un exemple avec la fonction dup()).
- Concernant la gestion des erreurs, voir le chapitre ici !
- Enfin, les descripteurs de fichiers sont hérités par les processus fils.
Création d’un fichier
Cette fonction va permettre de créer un fichier. Il ne doit donc pas exister sur le disque ! Après création, il ne sera pas nécessaire d'ouvrir ce fichier. La fonction va en effet l'ouvrir en écriture seule et retourner un descripteur sur celui-ci.
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
int creat (char *path, int mode) ;
En entrée:
path représente un pointeur sur un tableau de caractères contenant le chemin d’accès et le nom du fichier.
mode indique les permissions du fichier (elles seront influencées par umask). Le mode peut être initialisé avec une constante octale 0666 par exemple. Voir aussi le chapitre sur les droits d'accès.
En sortie:
La fonction retourne –1 si une erreur s’est produite, sinon le numéro du descripteur du fichier. Le type de l’erreur système se trouve dans la variable errno.
Exemple :
int nf ;
nf = creat ("/tmp/fichier.dat", 0666) ;
Ouverture d’un fichier
Cette fonction va permettre d'ouvrir un fichier en précisant le type d'accès:en lecture, écriture ou lecture et écriture.
Cette fonction
permet d'ouvrir un fichier mais aussi de le créer éventuellement.
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
int open (char *path, int oflag, int mode) ;
En entrée:
path représente un pointeur sur un tableau de caractères contenant le chemin d’accès et le nom du fichier.
oflag représente le mode d’ouverture du fichier, ces modes peuvent
se combiner entre eux avec les opérateurs + ou |.
Les opérateurs sont :
Valeur | Désignation |
O_RDONLY | Ouvert pour la lecture seulement |
O_WRONLY | Ouvert pour l’écriture seulement |
O_RDWR | Ouvert pour la lecture et écriture |
O_APPEND | Ajout - l’écriture se fera toujours à la fin du fichier |
O_SYNC | Chaque écriture implique une sauvegarde physique des données |
O_CREAT | Si le fichier n’existe pas, il est créé avec les permissions de mode, sinon, il est vidé. (Attention, sur d'autres systèmes, il peut y avoir une erreur s'il existe). Voir alors O_TRUNC. |
O_TRUNC | Si le fichier existe, sa valeur est tronquée à 0. (Inutil sous Linux, dépendra des autres systèmes) |
O_EXCL | Il y aura erreur d’ouverture si le fichier existe déjà (à combiner avec O_CREAT) |
O_BINARY | Attention, ce mode existe que pour certains compilateurs. Ne pas oublier de le positionner pour obtenir la compatibilité avec UNIX. |
mode indique les permissions du fichier (elles seront influencées par umask). Le mode peut être initialisé avec une constante octale 0666 par exemple. Le mode réel du fichier dépendra également de la valeur de umask. Voir aussi le chapitre sur les droits d'accès.
En sortie
open retourne –1 si erreur s’est produite, sinon le numéro du descripteur du fichier. Le type de l’erreur système se trouve dans la variable errno.
Exemple :
int nf ;
nf = open ("/tmp/fichier.dat", O_RDWR + O_CREAT, 0666) ;
if (nf == -1)
printf ("erreur d’ouverture fichier n° %d\n", errno) ;
Fermeture d’un fichier
Lorsqu'un fichier est ouvert et qu'il ne sert plus dans votre application, il est important de le fermer (afin de libérer des ressources !). De même avant la fin de votre application, il sera plus propre de fermer votre fichier sous peine d'avoir un fichier à 0 octet en taille sinon !
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
int close (int desc);
En entrée:
desc qui est le descripteur du fichier (récupéré via create() ou open())
En sortie:
Si close() retourne –1, une erreur s’est produite et le code d’erreur est dans errno, sinon il retourne 0.
Exemple :
int nf ;
nf = open ("/tmp/fichier.dat", O_RDWR|O_CREAT, 0666) ;
…
close (nf) ;
Ecrire dans un fichier
write permet d’écrire les n octets à partir d'un emplacement en mémoire dans le fichier ayant le descripteur desc.
Les données
seront enregistrées à partir de la position courante dans le fichier.
Si vous venez de créer ou d'ouvrir le fichier, ce sera à partir
du début du fichier (à moins que vous ayez ajouté O_APPEND
lors de l'ouverture par open() dans ce cas, les données seront ajoutées
à la fin de votre fichier) ou que vous vous soyez déplacé
dedans (par des appels précédent aux fonctions write() ou read()
ou encore en repositionnant le pointeur via la fonction lseek())
#include <errno.h>
#include <unistd.h>
int write (int desc, char *buf, int n);
En entrée:
desc qui est le descripteur du fichier (récupéré via create() ou open()).
buf qui est un pointeur sur le début de la zone mémoire contenant les données à enregistrées.
n qui représente le nombre d'octets à sauver.
En sortie:
write() retourne –1 si erreur, avec le type d’erreur dans la
variable errno. Sinon elle retourne le nombre de caractères réellement
écrit dans le fichier
Exemple :
int nf, ret ;
char *nomfic="/tmp/fichier.dat";
char buff[256] ;
nf=open (nomfic, O_RDWR + O_CREAT, 0666) ;
…
ret = write (nf, buff, 256) ;
if (ret !=256)
/* traitement erreur écriture */
close (nf) ;
Lire dans un fichier
Le contenu d'un fichier pourra être chargé partiellement ou complétement en mémoire en utilisant la fonction read()
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
int read (int desc, char *buf, int n) ;
read permet d’écrire les n octets, en les stockant à l’emplacement pointé par buf, à partir du fichier ayant le descripteur desc.
read retourne –1 si erreur, avec le type d’erreur dans la
variable errno. Sinon elle retourne le nombre de caractères réellement
lus dans le fichier.
Exemple :
int nf, ret ;
char *nomfic= "/usr/tmp/fichier.dat" ;
char buff[256] ;
nf=open (nomfic, O_RDWR + O_CREAT, 0666) ;
…
ret = read (nf, buff, 256) ;
if (ret !=256)
/* traitement erreur lecture */
close (nf) ;
Se déplacer dans un fichier
Il est possible de positionner le pointeur courant du fichier n’importe où dans celui-ci afin d’opérer à une lecture ou écriture à un endroit précis. Nous utiliserons pour cela la fonction lseek()
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
int lseek (int desc, long dep, int mode) ;
En entrée:
desc qui est le descripteur du fichier (récupéré via un create() ou un open())
dep est un entier signé de type long, un déplacement peut être positif ou négatif. Si le déplacement dépasse la taille du fichier, celui-ci sera agrandi (si il y a permission d’écriture et ouverture en écriture).
mode indique l’origine de déplacement :
0 à partir du début du fichier
1 à partir de la position courante dans le fichier
2 à partir de la fin du fichier.
lseek retourne –1 si il y a erreur. La variable
errno contient le numéro de l’erreur. Sinon lseek retourne
la position courante dans le fichier.
Lors d’une écriture ou d’une lecture de n caractères,
le pointeur du fichier est incrémenté de n positions.
On pourra utiliser
cette commande pour déterminer la taille d’un fichier. Il suffira
alors de se déplacer de 0 octet à partir de la fin du fichier.
Exemple :
int nf ;
long ret ;
…
ret = lseek(nf,(long)0, 0) ; /* Déplacement en début de fichier
*/
…
ret = lseek(nf,(long)0, 2) ; /* Déplacement en fin de fichier, ret =
taille du fichier */
…
ret = lseek(nf,(long)200, 2) ; /* Si mode O_RDWR, le fichier sera augmenté
de 200 octets */
Dupliquer un descripteur de fichier
Il est possible de dupliquer un descripteur de fichier via la fonction dup().
Cette fonction retourne un nouveau descripteur de fichier dont les caractéristiques
suivantes sont identiques à l’original :
- même fichier ouvert
- même pointeur de fichier
- même droits d’accès
#include <fcntl.h>
#include <unistd.h>
int dup (int desc) ;
Le principal avantage de dup est d’utiliser le plus petit descripteur de fichier disponible. Cette faculté permet de remplacer les descripteurs standard (0, 1 ou 2).
Exemple :
/* But : remplacer l’entrée standard par un fichier */
int nf, nf1 ;
nf = open (« /etc/passwd », O_RDONLY) ;
. . .
nf1=dup(0) ; /* Sauvegarde de l’entrée standard */
close (0) ; /* Fermeture de l’entrée standard */
dup (nf) ; /* Place nf sur entrée standard*/
close (nf) /* Ce descripteur (nf) ne sert plus, 0 est un autre descripteur qui pointe sur le fichier */
gets (buf) ; /* Cette commande va lire le contenu du fichier et non plus le clavier */
printf (« %s », buf) ; /* Affichage à l’écran du contenu du fichier */
close (0) ; /* fermer le fichier ...*/
dup (nf1) ; /* On replace l’entrée standard (à priori c’était
le clavier) */
close (nf1) ; /* Inutile de garder deux descripteurs ... */
Effacement d’un fichier
Tous fichiers créés peuvent être supprimés (enfin, si vous avez les droits d'accès). Sinon il y aurait un problème de place au bout d'un moment...
Sous Unix,
la suppression d'un fichier n'implique pas forcement la suppression physique
du dit fichier sur le disque. En effet, s'il existe des liens sur ce fichier,
la suppression va effacer le nom, mais se contentera de mettre a jour le compteur
de lien sur le fichier. La plce ne sera donc pas récupéré
tant qu'il existera un lien sur ce fichier.
#include <errno.h>
#include <unistd.h>
int unlink (char *path);
En entrée:
path chemin et le nom du fichier à effacer. Cette fonction n'est pas une fonction récursive. Elle efface 1 et 1 seul fichier. A vous de faire le reste du code (Voir fonctions liées aux status d'un fichier et au contenu d'un répertoire).
En sortie:
unlink retourne –1 si une erreur s’est produite et le code
d’erreur est dans errno. Sinon, il retourne 0.
Exemple :
int ret ;
ret = unlink ("/tmp/fichier.dat") ;
if (ret)
{
printf ("erreur sur effacement fichier n° %d\n", errno) ;
}
Renommer un fichier
Changer le nom ou l'emplacement d'un fichier ou d'un répertoire (un
répertoire n'étant qu'un fichier particulier).
#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
En entrée:
oldpath ancien nom...
newpath nouveau nom du fichier.
Tous les autres liens matériels (créés avec link) restent inchangés.
En sortie:
rename renvoie 0 s'il réussit, ou -1 s'il échoue, auquel cas errno contient le code d'erreur.
Si newpath
existe déjà, il sera écrasé (si vous avez les permissions
!)
Si oldpath correspond à un lien symbolique, le lien est renommé;
si newpath correspond à un lien symbolique, le lien est écrasé.
Créer un lien sur un fichier
Il existe deux fonctions pour créer un lien sur un fichier:
la fonction link() dit lien matériel (hard link),
#include <unistd.h>
int link (const char *cible, const char *nom);
Ou mieux encore (soft link),
#include <unistd.h>
int symlink(const char *cible, const char *nom);
Le fichier sur
lequel on veut faire le lien peut ne pas exister.
En entrée:
cible est le nom du fichier sur lequel on veut faire le lien
nom est le nom du nouveau lien sur le fichier "cible"
En sortie:
Si réussite: 0 est renvoyé. En cas d'échec -1 est renvoyé,
et errno contient le code d'erreur.