Entrées Sorties
Le but de ce TD est d'utiliser les principales fonctions de manipulation d'entrées sorties en
C de la bibliothèque standard afin d'illustrer les notions de tampons d'entrées sorties.
Rappels de Shell
Tout processus (programme) possède trois canaux standards
stdin,
stdout et
stderr. La commande shell
A <f exécute la commande
A avec
stdin ouvert sur le fichier
f. Symétriquement,
A >f exécute
A avec
stdout ouvert sur le fichier
f. Enfin
A | B exécute les deux programmes
A et
B en parallèle, le canal
stdout de
A étant
redirigé sur le canal
stdin de
B.
Noter que les commandes
<f et
>f sont utilisables simultanément. Enfin
2>f redirige
stderr au lieu de
stdout.
Essayez l'effet de ces commandes avec les deux programmes suivants:
Copie élémentaire
On commence ce TD par mettre en place un environnement de travail propre:
- nouveau répertoire,
- télécharger le fichier copy1.c,
- écrire un Makefile pour compiler copy1.c en un programme copy1.
Essayez par exemple
copy1 < foo avec un fichier texte
foo arbitraire.
Effet des Tampons
Pour pouvoir expérimenter l'efficacité des tampons de fichiers, nous devons:
- mesurer le temps d'exécution des lectures/écritures,
- faire des mesures sur des fichiers de tailles variables.
Mesures de Temps
Modifier le programme
copy1.c pour qu'il imprimme sur
stderr le temps d'exécution de la boucle principale (
man times, voir
aussi le cours 6:
benchmark). Le temps qui nous interesse est le temps total
user+system.
Fichiers de Tailles Variables
Ecrire un programme
gen.c qui prend un entier
n sur la ligne de commande et affiche
n caractères sur sa sortie standard. Faites en sorte que
gen imprimme aussi son temps d'exécution sur
stderr, par exemple
GEN:456.
Tampons de Tailles Variables
Faire
man setvbuf, voir aussi le cours 6. Modifiez le programme
copy1.c pour qu'il prenne des entiers
p et
q sur la ligne de commande et qu'il affecte:
- un tampon de taille p sur stdin
- un tampon de taille q sur stdout
On
conservera l'impression du temps d'exécution sur la sortie d'erreur. Essayez plusieurs valeurs pour
n, p, q dans l'exemple suivant:
gen n | copy1 p q > logout
Le programme head
La commande
head n imprimme les
n premières
lignes de
stdin sur
stdout. Une ligne se termine par
'\n'.
- Ecrire ce programme head.c.
- Ecrire un programme genline.c similaire à gen.c mais qui écrit des lignes plutôt que des caractères. Par exemple genline n pourra écrire n fois la ligne Bonjour.
- Testez vos programmes avec des commandes du type:
genline p | head q
- Observez lorsque p>q et p<q.
On va maintenant obervez l'impact de
head sur
genline:
- Modifiez genline n pour qu'il imprime sur stout les lignes "ligne numero i" avec i de 1 à n au fur et à mesure de sa génération.
- Est-ce que head q modifie le moment ou se termine genline p ?
On peut confirmer ou pousser cette observation plus loin:
- Ecrire un programme neverdie.c qui imprimme indéfiniment la ligne Bonjour (on l'arrête avec Ctrl-C).
- Testez neverdie | head 5
Le programme sort
Ce programme lit toutes les lignes de
stdin, les range dans un tableau, puis ré-imprimme ces lignes sur
stdout dans l'ordre alphabétique.
Ecrire un tel programme en utilisant une bibliothèque de tri standard (
man qsort). Il faut procéder comme suit:
- allouer un tableau pour stocker les lignes avec malloc.
- allouer un buffer pour stocker une ligne avec malloc.
- lire N caractères depuis stdin dans le buffer (man fread).
- chercher la fin de ligne. Si elle n'est pas trouvée, augmenter la taille du buffer (man realloc) et lire N' caractères de plus.
- faire une copie de la ligne (man strdup ou man strcpy) dans le tableau des lignes. Eventuellement, agrandir le tableau des lignes si nécessaire.
- une fois la dernière ligne lue, trier le tableau (man qsort).
- imprimmer le tableau final sur stdout.
Le programme tail
La commande
tail n f imprimme les
n dernières lignes de fichier
f sur
stdout. C'est beaucoup plus difficile que pour
head ou
sort car il ne faut surtout pas charger tout le fichier en mémoire. Utilisez
fopen pour ouvrir le fichier.
On fait une lecture du fichier par petits blocs avec la commandes
fseek et
fread (
man!), en commençant par la fin du fichier. Il faut accumuler ces blocs de données (
man realloc,
man strcpy) jusqu'à trouver les
n dernières lignes.
Attention au cas de la dernière ligne!
Peut-on faire la même chose avec
stdin au lieu du nom de fichier
f ?