Mis à jour le 7 mars 2006
TD 2 : Entrées-sorties

TD 2 : Entrées-sorties

17 janvier 2006

1  Question de cours

On considère une application mini-base de données qui conserve des données persistantes dans un fichier data. On suppose que deux applications ne peuvent pas tourner en parallèle, mais seulement l'une après l'autre. Une session lit les données en mémoire, en modifie une partie puis réécrit le résultat dans le fichier data.

Quel est le problème essentiel auquel il faut penser?

(corrigé)
On voudrait qu'en cas de panne les données soit toujours correctes, ie. le fichier data contiennent les nouvelles données correctement écrites ou les anciennes données. Comment réaliser cela?
(corrigé)
La solution est nécessairement imparfaite. Expliquez pourquoi.
(corrigé)
Que manque-t-il pour s'en sortir?
(corrigé)

2  Copie de fichier

On désire écrire une commande mon_cat équivalente à la commande cat du système. Celle-ci copie sur sa sortie standard le contenu des fichiers dont les noms lui sont donnés en argument.

Écrire une fonction à deux arguments copy_data:Unix.file_descr -> Unix.file_descr -> unit, qui prend deux descripteurs de fichier en argument et copie tout ce qui peut être lu sur le premier dans le second, en utilisant les fonctions Unix.read et Unix.write.
(corrigé)
Écrire la commande mon_cat, en utilisant la fonction précédente sur chacun de ses arguments et en copiant dans la sortie standard (Unix.stdout). Pour simplifier, on considèrera toute erreur de lecture/écriture comme fatale.
(corrigé)

3  Entrée-sortie standard

On désire écrire une commande mon_tr qui remplace certains caractères reçus sur l'entrée standard avant de les retourner sur la sortie standard et qui a le même comportement que la commande tr du système (sans options). Par exemple, mon_tr abcd ijk remplace les caractères a, b et c par, respectivement, i, j et k et le caractère d par k.

Modifier la fonction copy_data précédente pour qu'elle prenne un argument supplémentaire, une fonction de type char -> char qu'elle applique sur chaque caractère lu avant de le copier.
(corrigé)
Écrire une fonction à deux arguments tr_function : string -> string -> (char -> char) qui retourne une fonction de traduction qui traduit chaque caractère qui lui sera passé en argument selon la correspondance définie par les chaînes de caractères reçues en premier et second argument de la fonction tr_function (la longueur d'une chaîne de caractères est retournée par String.length). Pour effectuer la traduction, on utilisera une chaîne de caractères de longueur 256 (créée par la fonction String.create 256) telle que le caractère à la position i dans cette chaîne (s.[i]) corresponde à la traduction du caractère de code i (Char.code c). Inversement, le caractère de code i est obtenu par Char.chr i.
(corrigé)
Ensuite, écrire la fonction principale qui lit sur l'entrée standard (Unix.stdin) et copie sur la sortie standard les données après traduction. Pour simplifier, on considèrera toute erreur de lecture/écriture comme fatale.
(corrigé)

4  Déplacement dans les fichiers

On désire écrire une commande mon_tail -n N file qui affiche les N dernières lignes du fichier régulier file. Pour cela, il faut lire le fichier en « sens inverse ».

Décrire le schéma général du programme.
(corrigé)
Écrire une fonction really_read qui force la lecture d'exactement size caractères sur un descripteur déjà ouvert et qui les place dans une chaîne de caractères à partir d'un indice passé en argument. Si la lecture n'est pas possible, lever une exception End_of_file.
(corrigé)
Écrire une fonction set_pos : Unix.file_descr -> int -> unit qui positionne le descripteur de fichier à la position donnée en argument et vérifie que la position d'arrivée est correcte. On utilisera la fonction Unix.lseek pour se déplacer dans le fichier.
(corrigé)
Écrire une fonction get_size : Unix.file_descr -> int qui retourne la taille du fichier associé à un descripteur.
(corrigé)
Écrire une fonction find_lines, telle que find_lines buffer size nb retourne l'indice de début de la (nb-1)-ème ligne en partant de la fin (size) de la chaîne de caractères buffer. Si la chaîne de caractères contient moins de nb retours à la ligne la fonction lèvera une exception contenant le nombre de lignes qu'il reste à lire.
(corrigé)
Écrire une fonction récursive tail, telle que tail desc size nb retourne la position absolue dans le fichier de la (nb-1)-ème ligne sur le descripteur desc à partir de la position absolue size. Attention, il faut faire attention à ne pas demander de lire plus que la taille disponible sur le descripteur sous peine que really_read lève une exception.
(corrigé)
Finalement, écrire la fonction tail_lines telle que tail_lines desc nb affiche les nb dernières lignes du descripteur desc, en ne tenant pas compte du dernier caractère du fichier. On pourra aussi utiliser la fonction copy_data des exercices précédents.
(corrigé)
On désire ajouter un option -f, équivalente à celle de la commande tail du système, qui permet d'afficher les modifications apportées au fichier au fur et à mesure quelles sont effectuées. Donner le principe ce cette fonction.
(corrigé)
Écrire une fonction monitor qui implante l'option -f. On pourra utiliser une fonction récursive intermédiaire qui affiche toutes les données lues entre la position courante du descripteur et la fin de fichier. On supposera pour cette fonction que le fichier ne peut pas être tronqué.
(corrigé)
Écrire une nouvelle fonction monitor qui prend en compte le fait que le fichier puisse être tronqué. Dans ce cas, la commande doit commencer à afficher les lignes à partir de la nouvelle taille du fichier.
(corrigé)
Terminer le programme. Attention, le fichier peut ne pas se terminer par un retour à la ligne.
(corrigé)

5  Lectures-écritures dans des fichiers

Écrire une commande mon_split n file qui découpe le fichier file en plusieurs fichiers (de noms file-0, file-1, etc.) de taille n (sauf éventuellement le dernier) et qui a un comportement équivalent à la commande split du système.

Pour réaliser cette commande, on suppose que la taille n peut être très grande et donc qu'il n'est pas envisageable d'allouer un tampon de cette taille pour lire puis écrire en deux opérations. On utilisera donc un tampon de taille fixe pour lire les données. Il faut alors faire attention au cas ou la taille des fichiers est inférieure à la taille des données lues.

On pourra en particulier modifier la fonction copy_data des exercices précédents en lui ajoutant en paramètre la quantité de caractères à copier, et en retournant le nombre de caractères non-copiés (quand la fin du fichier est atteinte).
(corrigé)

6  Exercice

Écrire une fonction mon_write qui se comporte comme write, sans utiliser celle-ci! —on utilisera seuleument single_write.
(corrigé)
Rappeler un des problèmes de single_write? Comment pourriez-vous corriger celui-ci?
(corrigé)



This document was translated from LATEX by HEVEA and HACHA.