next up previous contents index
Next: 9 Préprocesseur Up: Le langage C Previous: 7 Pointeurs

Subsections


8 Entrées-sorties

  Un fichier est constitué d'une suite de données et est vu par un programme C comme un flot i.e. une suite d'octets qui, selon le cas, constitue soit un fichier texte ou un fichier binaire. On peut alors comparer un fichier C à un tableau d'octets. Un fichier texte est un fichier structuré en suite de ligne et exploitable par divers programmes (editeur de texte, logiciel d'impression, etc ...), alors qu'un fichier binaire n'a aucune structure et est inexploitable par des programme externes qui ignorent la structure réelle du fichier.

8.1 Le tampon d'entrée-sortie

 Quelque soit l'opération que l'on veuille effectuer sur un fichier (lecture ou écriture), les données transitent par une zone mémoire temporaire appelé tampon avant d'être définitivement rangées dans le fichier. Le programmeur n'a pas à se soucier de la gestion de ce tampon; c'est le système qui en a la responsabilité. L'utilisation d'un tampon permet d'optimiser le fonctionnement des entrée-sorties puisque l'écriture ou la lecture dans un tampon (qui réside en mémoire) est bien plus rapide que la même opération sur le fichier directement.

Par défaut, à tout fichier est associé un tampon de taille prédéfinie. Cette taille est configurable par l'utilisateur à l'aide de la fonction setvbuf[*].

Le passage par un tampon, s'il accélère les opérations d'entrée-sortie, introduit un problème de décalage dans la chronologie, puisque l'opération effective de lecture ou d'écriture dans le fichier n'est pas prédictible par le programmeur. Il en découle qu'une intéruption brutale d'un programme peut conduire à des pertes d'infomations dans les fichiers[*].

8.2 Fonctions générales sur les flots

Les flots sont désignés par des variables de type FILE * (pointeur sur une structure de type FILE). Le type FILE est définie dans le fichier d'interface stdio.h. La défintion du type FILE est dépendant du système d'exploitation; mais il correspond à une structure (appelée descripteur) qui contient toujours les informations suivantes : l'adresse du tampon, pointeur vers le premier caractère libre du tampon, le nombre de caractères libres du tampon, l'état du fichier, etc ... Pour utiliser un fichier dans un pogrammme C, il faut donc inclure le fichier stdio.h et définir une variable par fichier.

 
#include 
FILE *fic1, *fic2;
...

fopen

Avant qu'un programme puisse utiliser un fichier, il faut ouvrir ce fichier. L'ouverture du fichier initialise tous les champs du descripteur évoqué plus haut. C'est la fonction
 
FILE *fopen(char *nom, char *mode_d_accès);
qui est utilisée pour ouvrir un fichier Cette fonction retourne un pointeur vers une structure de type FILE qui sera utilisée pour les opérations d'entrées-sorties par la suite. C'est ce pointeur qui identifie parfaitement le fichier que l'on veut utiliser. Le paramètre nom est une chaîne de carcatères qui désigne le nom du fichier. Le paramètre mode_d_accès est une également une chaîne de caractères qui spécifie le type d'opération que l'on veut effectuer sur ce fichier:

"r" Ouvrir le fichier en lecture seule
  (Erreur si le fichier n'existe pas)
"w" Ouvrir le fichier en écriture seule
  (Création du fichier si le fichier n'existe pas)
  (Si le fichier existe, son ancien contenu sera perdu)
"a" Ouvrir le fichier en ajout i.e. écriture à la fin du fichier
  (Création du fichier si le fichier n'existe pas)
"r+" Ouvrir le fichier en lecture et écriture
  (Erreur si le fichier n'existe pas)
"w+" Ouvrir le fichier en lecture et écriture
  (Création du fichier si le fichier n'existe pas)
  (Si le fichier existe, son ancien contenu sera perdu)
"a+" Ouvrir le fichier en lecture et en ajout
  (Création du fichier si le fichier n'existe pas)
   


Lorsqu'il y a une erreur, le programme ne s'arrête pas; la fonction renvoie la valeur NULL (qui vaut 0). C'est au programmeur de tester cette valeur chaque fois qu'il appelle cette fonction.

 
main() {
    FILE * flot;
    flot = fopen("xxx.c", "r");
    if (flot == NULL) {
        printf("Erreur d'ouverture du fichier %s", "xxx.c");
        exit(0);
    }
}
A priori, pour utiliser des fichiers binaires, il faut ajouter le caractère b dans ces chaînes ("ab+", "wb+","rb", etc...) mais certains systèmes s'en passent.

fclose

Même si le système se charge de fermer tous les fichiers ouverts à la fin de l'exécution d'un programme, il est vivement conseillé de fermer les fichiers dès qu'ils ne sont plus utilisés notamment pour réduire le nombre de descripteurs ouverts et se prémunir contre toute interruption brutale du programme. C'est la fonction
 
int fclose(FILE *flot);
qui est utilisée pour fermer un fichier. Le paramètre flot est une pointeur vers une structure de type FILE, qui est presque toujours la valeur retounée par un appel à la fonction fopen. La fonction fclose retourne EOF (End Of File) si la fermeture du fihcier s'est mal passée. Sinon, c'est la valeur 0 qui est retournée.

fflush

Cette fonction provoque l'écriture physique immédiate du tampon à la demande et non lorsque le système le décide (voir 8.1). Elle rend EOF en cas d'erreur et 0 dans les autres cas.

 
int fflush( FILE *flot);

tmpfile

La fonction tmpfile crée un fichier temporaire dans le répertoire spécifié par la constante symbolique T_tmpdir (en général /tmp) définie dans le fichier stdio.h. Ce fichier est créé en mode lecture/écriture (wb+) et est automatiquement fermé en fin de programme.

Cette fonction retoune le pointeur vers le flot ou NULL en cas d'erreur.

 
FILE *tmpfile (void);

setvbuf

Cette fonction redéfinit la politique de gestion du tampon d'entrée-sortie.

 
       int setvbuf( FILE *flot, char *buf, int mode , size_t taille);
Il y a trois choix dans la politique de gestion des tampons :

Normallement, tous les fichiers sont associés à des tampons bloc. La fonction setvbuf peut être utilisée pour changer le type de tampon utilisé. Le paramètre mode doit être l'une des trois constantes symboliques:

_IONBF:
sans tampon
_IOLBF:
tampon de type bloc
_IOFBF:
tampon de type ligne

A l'exception des fichiers sans tampon, le paramètre buf doit être un pointeur vers un tampon de taille plus grande ou égal à taille. C'est la mémoire fournie par la varibale buf qui sera alors utilisé comme tampon. Si le pramètre buf est la constante NULL, la fonction alloue son propre espace pour le tampon. La fonction setvbuf ne doit pas être utilisée lorsque le tampon n'est pas vide: elle doit être appelée avant toute opération d'entrée-sortie (et après un fopen) ou immédiatement après un appel à fflush.

feof

La fonction feof teste l'indicateur de fin de fichier du flot spécifié par le parmètre flot et retourne une valeur non nulle.

 
int feof(FILE * flot);
Cette fonction doit être appelée immédiatement après une opération d'entrée-sortie pour savoir si cette opération a échoué ou pas. La variable globale errno définie dans le fichier errno.h contient un code permettant connaître plus précisement la nature de l'erreur.

8.3 Les unités standard d'entrée-sortie

Par défaut, lorsqu'un programme débute, il existe trois flots prédéfinis qui sont ouverts: il s'agit de stdin, stdout et stderr. Ces flots sont connectés (comme leur nom le laisse supposer) sur les organes d'entrée-sortie naturels:

Même si un programme n'utilise que ces trois flots prédéfinis, sous le système UNIX, il est possible (sans rien modifier au programme) de modifier des flots par défaut en utilisant les redirections sur la ligne de commande du lancement du programme. De même, il est possible de rediriger les sorties d'un premier programme vers l'entrée d'un deuxième programme à l'aide des tubes. Pour plus de précision, se reporter au polycopie Introduction à UNIX.

8.4 Lecture et écriture en mode caractère

fputc.

La fonction fputc transfère le caractère car dans le fichier réprésenté par le paramètre flot. La valeur de retour de cette fonction est le caractère car ou EOF en cas d'erreur.
 
int fputc(int car, FILE *flot);

fgetc.

La fonction fgetc retourne le caractère lu dans le fichie r représenté par le parmètre flot. En cas d'erreur ou de fin de fichier, elle retourne la valeur EOF.
 
int fgetc(FILE *flot);
On remarquera que le type du retour de la fonction est un int (et non pas un char). Se reporter à la section 1.4.2, le paragraphe "caractère impossible" pour plus de précision.

Les macros putc et getc.

Les macros
 
int putc(int car, FILE *flot);
int getc(FILE *flot);
font exactement la même chose que fputc et fgetc mais leur différence vient du fait qu'elles sont (pas forcément toujours) des macros donc plus efficaces. Il faut donc les utiliser avec précaution et éviter les effets de bord intenpestifs.

getchar.

getchar() est un synonyme de getc(stdin).
 
int getchar(void);

putchar.

putchar(c) est un synonyme de putc(c, stdout).
 
int putchar(int c);

8.5 Lecture et écriture en mode chaîne

fgets.

La fonction fgets lit une chaîne de caractères dans un flot et le range dans un tampon défini (et alloué !!!) par l'utilisateur.
 
char *fgets(char *s, int taille, FILE *flot);
Plus précisément, cette fonction lit taille-1 caractères dans le fichier de descripteur flot ou bien jusqu'au caratère '\n', s'il survient avant les taille-1 caractères, (le caractère '\n' est copié dans le tampon). La fonction rend le pointeur vers le début du tampon ou NULL en cas d'erreur ou de fin de flot. Comme pour la fonction fgetc, le fonction feof permet de vérifier s'il s'agit d'une erreur ou d'un fin de fichier. Le dernier caractère du tampon est '\0'

fputs.

La fonction fputs écrit une chaîne de caractères dans un flot. Cette fonction n'écrit pas le caractère '\0' .

 
int fputs(const char *s, FILE *flot);
La fonction retoune une valeur non négative si tout s'est bien passé. La valeur EOF indique une erreur.

gets.

Comme on peut le voir sur le prototype de la fonction, le flot ainsi que le nombre maximum de caractères à lire ne sont pas spécifiés.
 
char *gets(char *s);
Le flot par défaut est stdin. La fonction se comporte comme mais il n'y pas de test de débordement et le caractère de fin de ligne '\n' est remplacé par '\0'.

puts.

La fonction puts écrit sur le flot stdout la chaîne de carcatères s.

 
int puts(char *s);
La fonction retourne une valeur non négative si tout s'est bien passé. La valeur EOF indique une erreur. Contrairement à la fonction fputs, la fonction ajoute le caractère '\n' à la fin de l'écriture.

8.6 Lecture et écriture formatée

Les fonctions fprintf et fscanf permettent d'effecuter des lectures et écriture formatées de données dans un flot. Les

 
int fprintf(FILE *flot, char *format, arg1, ...,  argN);
fonctions printf et scanf sont des raccourcis pour les fonctions fprintf et fscanf lorsque le flot est l'entrée standard ou la sortie standard.

8.6.1 Ecriture formatée avec printf

Nous avons déjà rencontré ce type d'écriture dans les divers exemples des chapitres précédants. Le format général de la fonction printf est de la forme:

 
int printf(const char *format, arg1, ...,  argN);
La fonction printf affiche les arguments donnés en fonction du format spécifié, et ce après conversion et mise en forme si nécessaire. Cette fonction retourne le nombre de caractères affichés.

Le format est une chaîne de caractères qui contient deux types d'objets: des caractères ordinaires, qui sont affichés sans aucune modification, et des spécifications, qui déterminent le type de conversion et de mise en forme à effectuer avant l'affichage proprement dit.

Toutes les spécifications commencent par le caractère % et se terminent par un un caractère de conversion. Entre ces deux caractères, on peut placer dans l'ordre:

1.
des Drapeaux
- :
justifie à gauche l'argument correspondant
+ :
impression du signe du nombre
espace:
place un espace au début
0 :
complète (si besoin) le début du champ par des zéros (pour les arguments numériques).

2.
Un nombre qui précise la largeur minimum du champ d'impression.
3.
Un point qui sépare la largeur du champ et la précision demandée.
4.
Un nombre qui spécifie la précision du champ.
5.
une lettre qui modifie la largeur du champ (h pour short, l pour pour long et L pour long double)

d, i notation décimale signée int
u notation décimale non signée int
o notation octale non signée int
x, X notation hexadécimale non signée sans les caractères 0x ou 0X int
c caractère après conversion en unsigned char int
s impression d'une chaîne de caractères se terminant pas par le carcatère de fin de chaîne char *
f notation décimale de la forme [-]mmm.ddd double
e, E notation décimale de la forme [-]m.dddddde + ou - xx double
% imprime le caractère %  

float x = 123.456;

printf(">%f< ", x);                 >123.456001<
printf(">%12f< ", x);               >  123.456001<
printf(">%12.2f< ", x);             >      123.46<
printf(">%.2f< ", x);               >123.46<
printf(">%-12.2f< ", x);            >123.46      <
printf(">%+-12.f< ", x);            >+123        <
printf(">% -12.2f< ", x);           > 123.46     <
printf(">%012.2f< ", x);            >000000123.46<
printf(">%.0f< ", x);               >123<
printf(">%#.0f< ", x);              >123.<

Pour de plus de précision, consulter les pages du manuel UNIX.

8.6.2 Ecriture formatée avec scanf

Le format général de la fonction scanf est de la forme:

 
int scanf(const char *format, arg1, ...,  argN);
Cette fonction lit sur le flot, en fonction des spécifications du format, les données pour les affecter aux arguments forunis. Cette fonction rend EOF en cas d'erreur ou si la fin de flot est atteint. Sinon, elle retourne le nombre d'objets lus.

Le format contient de spécifications comme pour le format de printf. Cette chaî ne peut contenir:


 
Figure 8.1: Caractères de conversion pour scanf
\begin{figure}\begin{tabular}{\vert\vert c\vert l\vert l\vert\vert}\hline\hline
...
...ant les
caractères indiqués & char * \ \hline\hline
\end{tabular}
\end{figure}

Pour de plus de précision, consulter les pages du manuel UNIX.

8.7 Les entrées sorties de bas niveau

Jusqu'à présent, les fonctions d'entrée et de sortie que avons évoqués sont de haut niveau; il sont indépendant des systèmes d'exploitation quea l'on utilise.Nous allons nous intéresser, à présent, à des fonctions de plus bas niveau et qui peuvent différer d'un système à l'autre. Ce qui va suivre s'applique pour tous les systèmes d'exploitation UNIX.

Comme nous l'avons déjà dit, au démarrage d'un programme, trois fichiers standards sont ouverts: stdin, stdout et stderr dont les descripteurs sont respectivement 0, 1 et 2. On peut donc utiliser ces fichiers sans avoir à les ouvrir, comme c'est le cas pour tout autre flot.

8.7.1 Les fonctions open, create et unlink

Les fonctions de bas niveau open et create permettent d'ouvrir un fichier:

 
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
Ces fonction retournent un descripteur de fichier qui est un entier. Le paramètre flags a pour valeur O_RDONLY, O_WRONLY ou O_RDWR selon le l'on veuille utiliser le fichier en lecture seule, en écriture seule ou en lecture et écriture.

Le paramètre flags peut également être un entier vu comme une suite de bits que l'on postionne avec des ou (|) Les bits à positonner sont:

Le paramètre mode définit les permissions (mode & ~umask) du fichier (en cas de création). Généralement la valeur de mode est 0.

Pour plus de précisions, se reporter aux pages du manuel en ligne UNIX.

Pour utiliser ces constantes, il faut inclure le fichier

 
#include 
#include 
#include 

La fonction create sert à créer un fichier s'il n'existe pas ou à réécrire d'anciens fichiers. Les permissons sont données en notation octale (se preporter au polycopié UNIX pour plus de précisions).

8.7.2 Les fonctions read et write

Les entrées-sorties de haut niveau sont toutes bâties par des appels systèmes (fonctions de bas niveau) read et write.
 
int read(int fd, char *buf, int count);
int write(int fd, const char *buf, int count);             
La fonction read lit, dans le fichier déisgné à partir du descripteur de fichier fd, count octets et les range dans le tableau de caractères buf.

La fonction read écrit, dans le fichier déisgné par le descripteur de fichier fd, count octets du tableau de caractères buf.

Ces fonctions renvoient le nombre d'octets effectivement transférés. On peut donc demander à lire ou écrire n octets mais si le nombre d'octets disponibles est inférieur à n , les fonctions read et write retournent le nombre d'octets effectivement transférés. La valeur 0 est retournée lorsqu'on atteint une fin de fichier et -1 pour toute autre erreur.

Pour pouvoir utiliser ces fonctions il faut inclure les fichiers .h suivants:

 
#include 
#include 

Voici un petit exemple d'utilisation de ces fonctions: il s'agit d'un programme qui lit à la console une suite de caractères et qui les réécrit à la console.

 
int main() {
    char buf[10];
    int n;
    while (n = read(0, buf, 10))
        write(1, buf, n);
    return 0;
}


next up previous contents index
Next: 9 Préprocesseur Up: Le langage C Previous: 7 Pointeurs

Touraivane
9/21/1998