Faites vous un fichier prêt à utiliser pour les TPs (ou la vie de tous les
jours ;) ) : vide.c
.
#include <assert.h>
#include <stdio.h> /* printf, fopen, perror, ... */
#include <stdlib.h> /* exit, malloc, atoi, ... */
#include <string.h> /* strlen, memcpy, ... */
int main(int argc, char *argv[])
{
return EXIT_SUCCESS;
}
On pourra rajouter la fonction
exit_if
proposé ci-après
Une assertion arrête le programme si elle n’est pas vérifié. On peut de plus les activer ou les désactiver à la compilation (ceci permet de fournir des version de production). Comme on peut les désactiver, le bon fonctionnement de votre programme ne doit JAMAIS en dépendre.
Pour utiliser les assertion il faut inclure
assert.h
.
void foo(char *ptr, int min, int max)
{ // Exemple d'utilisation
assert(ptr); // Le pointeur ne doit pas etre null
assert(min <= max); // Min ne doit pas etre superieur a max
// - Où devraient être ces commentaires ?
// ...
}
Vous pouvez aussi créer votre propre fonction d’“arrêt”. A la différence d’une assertion classique, on ne peut pas désactiver cette fonction à la compilation.
void exit_if(int condition, const char *prefix)
{
if (condition) {
perror(prefix);
exit(EXIT_FAILURE);
}
}
perror
affiche un message correspondant à la dernière erreur rencontré (éventuellement préfixé parprefix
) sur la sortie d’erreur. C’est globalement équivalent àfprintf(stderr, "%s: %s\n", prefix, strerror(errno));
void foo(char *fname)
{ // Exemple d'utilisation
FILE *fd = fopen(fname, "r");
exit_if(fd == NULL, "foo:fopen");
// - essayez aussi avec exit_if(fd == NULL, fname);
// - peut on remplacer `exit_if` par `assert` ?
// - peut on cumuler `exit_if` et `assert` ?
// ... do something with fd
}
Bien que cette fonction ne soit clairement pas efficace, elle facilite grandement la clarté de votre code. Il existe (hors du cadre du cours) des moyens pour la rendre plus efficace, vous pouvez donc vous en servir sans scrupules ni mauvaise conscience.
write
Écriture et création de fichiers (
fopen
,fprintf
/fputs
,fclose
)
Pour se chauffer, nous proposons d’écrire le hello world de la manipulation
de fichier, i.e., un programme qui crée le fichier donné en argument (via
argv
) avec Bonjour monde.
comme contenu.
Remplacez le texte par hello world
, que constate t on ? Bonus une
amélioration simple consiste a prendre en argument le texte à écrire.
Que se passe t il si le fichier existe déjà ? Comment y remédier (man
fopen
) ? Bonus rajouter l’option -f
a votre programme pour qu’il
effectue quand même l’action si le fichier existe). Profitez en pour
rajouter l’option -a
pour ajouter (append
) à la fin du fichier.
cat
Lecture/Écriture caractère par caractère (
fgetc
/fputc
)
On va essayer de reprogrammer la commande cat
(man 1 cat
).
Dans un premier temps, on ne considère qu’un fichier en entré (argv
).
Quel genre d’erreur peuvent survenir, et comment s’en protéger
Les réponses à ces deux questions sont données par les exercices suivant.
cat
, si le nom de fichier est -
, on utilisera
l’entrée standardhead
Lecture ligne par ligne pour les fichiers textes (
fgets
/fputs
)
Nous essayerons cette fois de reprogrammer la fonction head
qui affiche les
n
(par défaut 5) premières lignes d’un fichier. Modifier le programme
précédent pour rajouter le compteur de ligne rajoute beaucoup de bruit (une
nouvelle boucle, donc une double boucle). Nous proposons donc d’utiliser la
fonction fgets
pour lire ligne par ligne.
head
Si on regarde plus sérieusement le code écrit, on peut (on doit !) se demander
ce qu’il ce passe si la ligne est plus longue que le buffer
? (Essayez en
réduisant la taille du tampon). Sachant que fgets
lit une ligne (terminée par
un \n
), pourquoi le \n
fait parti du résultat ?
Bonus: Corrigez votre programme pour qu’il marche avec des lignes de tailles arbitraires.
Bonus: La commande tail
est un peu l’opposé de la commande head
, elle
montre les n
dernières lignes d’un fichier. Attention cette commande est
beaucoup plus complexe à réaliser ! Remarque: le problème soulevé par la
taille du tampon de fgets
est hautement exacerbé par tail
. Sans le
faire, réfléchissez à pourquoi y a t il une telle augmentation de
difficulté.
cp
(Copie de fichier)Lecture bloc par bloc pour les fichiers binaires (
fread
/fwrite
)
Écrire une fonction int copy(FILE *dst, FILE *src)
qui copie un fichier par
blocs (fread
, fwrite
). Cette fonction retournera 0 en cas de succès.
Écrire une fonction int cp(const char *dst, const char *src)
qui copie un
fichier. Cette fonction retournera 0 en cas de succès.
Écrire le programme copie
(de fichiers) qui acceptera deux paramètres
source et destination.
Essayez de faire varier la taille des blocs et comparer le temps de copie pour un gros fichier.
sfv
(à la maison)Le Simple File Verification (SFV) est un format de fichier créé pour permettre le stockage d’une somme de contrôle (le contrôle de redondance cyclique : CRC32 checksums) et ainsi permettre la vérification de l’intégrité d’un ou plusieurs fichiers. Wikipedia
sfv
et places vous y.crc32
sfv_generator
) qui calcule le crc32 de chacun de ses
paramètres (sauf le premier) et les sauvegardes dans un fichier (le premier
paramètre). Le format du fichier est le suivant :nom_de_fichier crc32
En utilisant les règles par défaut de
make
, unMakefile
est très simple a écrire (votre fichier est censé s’appelersfv_generator.c
) :
sfv_generator: sfv_generator.o crc32.o
Vous pouvez bien entendu préfixer votre Makefile
par vos options de
compilations préférées (en première ligne du fichier).
CFLAGS=-std=c99 -Wall
Passons maintenant au programme sfv_verify
qui vérifiera pour chacun des
fichiers passés en paramètre s’ils sont correct (c’est-à-dire que leur crc
est le même que celui spécifié dans le fichier).
Pour lire le fichier de données, vous pouvez vous servir de fscanf()
sfv_verify: sfv_verify.o crc32.o