Les entrĂ©es/sorties (E/S ou I/O) sâappliquent sur:
- Les flux standards:
- Entree standard (stdin, n°0)
- Sortie standard (stdout, n°1)
- Sortie dâerreur (stderr, n°2)
- Des fichiers en utilisant des objets appelés descripteurs de fichiers
Exemple de redirection:
ls 2> /dev/null
: permet de rediriger le flux stdout
vers /dev/null
.
Les entrées sorties peuvent se faire en utilisant soit les fonctions de haut-niveau ou bas-niveau.
Principe général
Pour faire des entrées/sorties, on va:
- Ouvrir le fichier (en lecture ou en écriture). On obtient un descripteur
- OpĂ©rations de lectures et/ou dâĂ©critures en utilisant le descripteur de fichier.
- Fermer le descripteur de fichier
Les lectures/écritures peuvent se faire de maniÚre formattées ou non.
Indispensable
Il est NECESSAIRE de sâassurer que le fichier est bien Ă©crit sur son support. Sinon des donnĂ©es vont ĂȘtre perdues/corrompues.
APIs
Fonctions haut-niveau
Ces fonctions sont fournies par la bibliothĂšque standard libc. Elles sont indĂ©pendantes du systĂšme dâexploitation, câest une interface accessible sur tout les systĂšmes courants.
Libc fourni également les descripteurs de fichiers, qui sont de type File*
Bas niveau
A ce niveau, les fonctions sont fournies par le systĂšme et sont dĂ©pendantes de chaque systĂšme mais permettent des opĂ©rations plus spĂ©cifiques. (ex. permissions, pipelinesâŠ)
Formattées vs non-formattées
Par formattĂ©, on entend par lĂ que les donnĂ©es ont Ă©tĂ© converties dâune reprĂ©sentation binaire en mĂ©moire Ă une reprĂ©sentation texte dans le fichier.
Au contraire, non-formatté signifie que les données sont au format binaire.
Exemple:
La valeur 12 reprĂ©sentĂ©e en mĂ©moire sur un octet sera prĂ©sentĂ©e au format texte avec 2 caractĂšres: â1â et â2â.
On aura donc 00001100
en binaire, et 00110001 00110010
au format texte
Résumé
Formatté = texte
Non-formatté = binaire
Descripteurs de fichiers
Work in progress
En bas niveau, descripteurs de fichiers = type int
API Haut-niveau
Documentation: page de man
Au haut-niveau, on doit importer la librairie stdio
de cette façon:
#include <stdio.h>
stdio = standard i/o
Pour ouvrir un fichier:
File* fopen (const char* name, const char* mode)
Avec les arguments suivants:
- name: nom du fichier Ă ouvrir
- mode: Le mode de lecture
r
: lecturew
: écriturer+
: Lecture/écriturew+
: Lecture/écriture, Le fichier est tronqué. (Le fichier sera vidé)a
: écriture en ajout à la fina+
: lecture/écriture en ajout
La fonction fopen
renverra un descripteur de fichier ou la valeur NULL si il y a une erreur.
Variantes:
fdopen
freopen
Fermer un fichier
int fclose(File* f)
Permet de fermer le descripteur donné.
Retournera 0
en cas de succĂšs et EOF
en cas dâĂ©checs
Documentation sur EOF: wikipedia.org
Exemple: Ouvrir un fichier âtoto.txtâ
En lecture:
File *f;
f = fopen("toto.txt", "r");
if (f == NULL) {
perror("fopen");
exit(-1);
}
// opérations de lectures...
if (fclose(f)) { // car si succĂšs, alors = 0 donc false
perror("fclose");
}
En lecture/écriture:
Par octet:
// lecture
int getc(File* stream)
int fgetc(File* stream)
int getchar(void)
// écriture
int putc(int c, File* stream)
int fputc(int c, File* stream)
int putchar(int c);
Par bloc:
size_t freed(void *ptr, site_t size, size_t nmemb, File* stream)
size_t fwrite(const void *ptr, size_t size, size_t nmemb,File* stream)
Lecture (écriture) de nmemb
objets de taille size
vers mémoire pointée par ptr
depuis (vers) le descripteur Stream
Exemple
// lecture
int bob[50];
size_t n;
n = fread(bob, sizeof(int), 50, f)
if (n != 50) { /* message */ }
// avec des char
char ligne[50]
size_t nl;
nl = fread(ligne, 1, 50, f) // char de taille '1'
if (n != 50) { /* message */ }
Formatées
// sur stdout
int printf(cpnst char* format, ...)
// print sur le fichier de descripteur F
int fprintf(File* f, const char* format, ...)
// En mémoire à l'adresse pointée par S.
int sprintf(char* s, const char* format, ...)
// comme sprintf mais écrit au plus 'n' caractÚres
int snprintf(char* s, size_t n, const char* format, ...)
Méthode
sprintf
à éviter
// Depuis stdin
int scanf(const char* format, ...)
// Depuis le fichier 'f'
int fscanf(File* f, const char* format, ...)
// Depuis la chaßne à l'adresse primaire pointée par 's'
int sscanf(const char* s, const char* format, ...)
Remarques TP
Exercice 1
Consignes:
Ăcrire une fonction :
int copie(FILE *entree, FILE *sortie)
qui copie le contenu du fichier de descripteur entree dans le fichier de descripteur sortie. La fonction doit retourner le nombre dâoctets copiĂ©s, ou -1 en cas dâerreur. La copie peut se faire par caractĂšre avec getc() / putc() ou bien par bloc avec fread() / fwrite().
Tester cette fonction en Ă©crivant un programme principal lâutilisant avec la copie de deux fichiers : un fichier texte (par exemple le code source du programme), et un fichier binaire (par exemple un fichier exĂ©cutable). Vous pourrez utiliser la commande cmp(1) pour vĂ©rifier que la copie est conforme Ă lâoriginal.
Code:
#include <stdio.h>
#include <stdlib.h>
// Retourne le nombre de caractÚres copiés ou -1 si erreur
int copie(FILE *entree, FILE *sortie) {
int nb_copies = 0;
int c = getc(entree);
while (c != EOF){
if (putc(c, sortie) == EOF)
break; // erreur, putc a échoué
c = getc(entree);
nb_copies++;
}
if (ferror(entree) || ferror(sortie)) {
return -1;
} else return nb_copies;
}
int main(int argc, char *argv[]) {
if (argc != 3) {
fprint(stderr, "Usage: %s entree sortie\n", argv[0]);
exit(1);
}
// arguments de la ligne de commande
const char *fichier_entree = argv[1];
const char *fichier_sortie = argv[2];
FILE *entree = fopen(fichier_entree, "r");
if (entree == NULL) {
perror("fopen(entree)");
exit(2);
}
FILE *sortie = fopen(fichier_sortie, "w");
if (sortie == NULL) {
perror("fopen(sortie)");
exit(2);
}
int n = copie(entree, sortie);
if (n >= 0){
printf("Copie de %d caractĂšre(s)\n", n);
} else {
printf("Erreur de copie\n");
}
// fermeture des fichiers
if (fclose(sortie) != 0) {
perror("fclose(sortie)")
}
if (fclose(entree) != 0) {
perror("fclose(entree)");
}
return 0;
}
Bas-niveau
Lâinterface est fournie par le systĂšme dâexploitation. Les descripteurs sont de type int
.
En particulier:
0
pour lâentrĂ©e standard (STDIN_FILE)1
pour la sorte (STDOUT_FILE)2
pour la sortie dâerreur (STDERR_FILE)
Pipelines
TODO