Sommaire
Ecrire un fichier
Lire un fichier
Opérer sur un fichier
I) Ecrire un fichier
Lorsque vous définissez un objet dans la boucle interactive, il existe tant que la fenêtre reste ouverte. Si vous la fermez puis relancez la boucle, Caml aura perdu toutes les informations précédentes. Le même phénomène a lieu si votre programme est compilé : si vous le fermez puis le réexécutez, il recommencera à zéro. C'est finalement le même phénomène qu'avec votre ordinateur lui-même : si vous tapez un texte mais que vous éteignez l'ordinateur sans sauvegarde, le texte sera perdu au démarrage suivant. Mais alors que faire si vous souhaitez que l'utilisateur de votre jeu puisse sauvegarder des parties, ou si vous voulez enregistrer les meilleurs scores ? Il va falloir enregistrer ces informations sur un fichier du disque dur.
La première chose à faire est de créer ce fichier : on utilise pour cela la fonction open_out. Vous allez donner comme argument à cette fonction un nom de fichier sont forme de chaîne de caractères. Il y a quatre choses importantes à propos de cette fonction :
1) Vous pouvez directement donner le nom du fichier sans répertoire : let canal_sortie = open_out "scores.txt";;. Dans ce cas, le fichier sera créé dans un répertoire par défaut. Il est probable que ce soit le répertoire d'installation d'OCaml, ou un répertoire du type "Mes documents" pour Windows XP, voire le répertoire dans lequel s'exécute votre programme si vous l'avez compilé. Mais vous pouvez aussi donner le chemin d'accès complet du fichier, vous êtes alors sûr de l'endroit où il sera, si tant est que le répertoire donné existe bien. Par exemple : let canal_sortie = open_out "C:/scores.txt";;. (Du moins, il faut que l'utilisateur ait bien un disque nommé C, mais c'est quand même courant).
2) Pour les utilisateurs de Windows, vous aurez sûrement remarqué en explorant votre disque dur que pour une raison mystérieuse, Windows utilise des antislashs (\) pour les adresses, contrairement à l'usage courant qui veut qu'on utilise un slash normal (/) comme sur Internet par exemple et comme pratiquement tous les autres systèmes d'exploitation. Ainsi, si vous donnez un chemin d'accès complet à Caml, vous devez séparer les répertoires avec des slashes. Cette remarque est valable pour toutes les autres fonctions utilisant des noms de fichiers.
3) Vous avez remarqué que j'ai créé un fichier texte (txt). En fait, vous pouvez utiliser n'importe quelle extension, qu'elle existe officiellement ou non, et même ne pas préciser d'extension ! Cependant, l'usage courant est d'utiliser txt car cela perturbera moins l'utilisateur qui verra ce fichier sur son disque (ne seriez-vous pas étonné(e) de voir dans votre mémoire un fichier en extension exe alors que vous n'avez pas installé de programme récemment ? Ne seriez-vous pas tenté(e) d'y voir un parasite ?).
4) Enfin, sachez que la fonction open_out se comporte ainsi : si le fichier donné en argument n'existe pas, il est créé. Si un tel fichier existe, alors il sera écrasé par le nouveau. Evitez donc d'utiliser des fichiers comme "C:/Program Files/Mozilla Firefox/firefox.exe", l'utilisateur du programme risque de ne pas vous le pardonner ! Une bonne solution est en fait, avant d'utiliser open_out, de vérifier si le fichier que vous voulez créer existe déjà (voir plus bas) et si c'est le cas de demander à l'utilisateur s'il est d'accord pour écraser le fichier existant.
Bon vous avez créé votre fichier. Vous recevez en résultat un objet que j'ai ici choisi de nommer canal_sortie... parce que c'est un type d'objet qu'on appelle un canal de sortie, c'est-à-dire un objet qui permettra à Caml d'envoyer des choses vers l'extérieur. Vous constaterez que son type est « out_channel ». Il faut maintenant écrire dans ce canal, sinon votre fichier va rester tout vide. Pour cela, on utilise la fonction output. Elle demande quatre argument : un canal de sortie, une chaîne de caractères s et deux entiers n et l. Et que fait-elle ? Elle va écrire dans votre canal de sortie, à la suite de ce qui est déjà éventuellement écrit, la sous-chaîne de s qui commence au caractère numéro n et contient l caractères (autrement dit, elle inscrit la chaîne String.sub s n l). Et si je veux utiliser plusieurs lignes ? En effet, cela facilitera la lecture ultérieure (car il y a une fonction pour lire ligne par ligne, voir plus bas). Tout simplement, j'insère dans ma chaîne de caractères un caractère spécial ; '\n', qui signifie justement « saut de ligne ».
Par exemple, je veux écrire dans mon fichier « C:/scores.txt » les textes « Humain : 174 points » et « Ordinateur : 156 points », chacun sur une ligne. Je peux alors taper output canal_sortie "Humain : 174 points\nOrdinateur : 156 points" 0 43;; ou si je veux le faire en deux fois :
output canal_sortie "Humain : 174 points\n" 0 20;;
output canal_sortie "Ordinateur : 156 points" 0 23;;
Et maintenant que j'ai tout écrit, il ne me reste plus qu'à fermer le canal de sortie, c'est-à-dire expliquer à Caml que je ne m'en servirai plus et qu'il peut détruire la connexion avec le fichier. On utilise simplement la fonction close_out avec comme argument le canal à fermer, par exemple ici : close_out canal_sortie;;. Il n'est pas obligatoire de fermer explicitement les canaux de sortie (ni les canaux d'entrée dont je parle dans le paragraphe suivant) car Caml les fermera de toute manière quand vous le quitterez. Cependant, il est conseillé de le faire pour des raisons de stabilité du système.
Vous constaterez deux choses si vous avez essayé toutes ces manipulations :
- Après l'appel à close_out, vous ne pouvez plus vous servir de canal_sortie dans la fonction output.
- Allez voir sur votre disque dur dans le répertoire « C:/ ». Normalement, vous y trouverez un fichier nommé « C:/scores.txt » avec dedans le texte « Humain : 174 points Ordinateur : 156 points » sur deux lignes ! Et ce fichier reste si vous arrêtez Caml, fermez votre programme ou redémarrez l'ordinateur. Mission accomplie !!!
II) Lire un fichier
D'accord mais tout ça n'a d'utilité que si vous pouvez lors d'une exécution ultérieure récupérer ces informations enregistrées. Et vous allez voir, on procède de manière très symétrique : on ouvre un canal (qui sera cette fois un canal d'entrée), on y lit les informations et on ferme le canal.
Pour ouvrir le canal, on utilise la fonction open_in avec encore comme argument le chemin d'accès au fichier. Notez que si le fichier n'existe pas (par exemple c'est la première fois que le joueur utilise le programme, il n'a donc pas enregistré de partie), aucun canal ne sera ouvert et vous aurez l'exception « Sys_error ». Lors de l'ouverture d'un canal, Caml place au tout début du fichier correspondant ce qu'on appelle un pointeur (vous allez voir son utilité).
Pour lire le canal quand il est ouvert, on a deux fonctions. La première permet de lire ligne par ligne, il s'agit de input_line. Cette fonction demande comme seul paramètre un canal d'entrée et vous rend deux possibilités :
- si le fameux pointeur est à la fin du fichier, vous avez fini la lecture et vous allez recevoir l'exception End_of_file.
- sinon, Caml parcourt le fichier jusqu'au prochain saut de ligne ou jusqu'à la fin du fichier (selon que le pointeur était ou non sur la dernière ligne), il place le pointeur à l'endroit où il finit (c'est-à-dire soit au début de la ligne suivante si le fichier n'est pas terminé, soit à la fin du fichier) et vous rend la chaîne de caractères qu'il a lue.
La deuxième fonction, input, est un peu plus compliquée. Vous devez d'abord avoir créé une chaîne de caractères. Puis input a besoin comme arguments d'un canal d'entrée, du nom de cette chaîne de caractères, et de deux entiers l et i. Elle fonctionne ainsi : elle tente de lire l caractères dans le fichier (en partant de la position du pointeur) et de les placer dans la chaîne en question, en partant du caractère numéro i de cette chaîne. Pourquoi ai-je dit « tente » ? Parce que deux choses peuvent arriver : il se peut que vous ayez demandé trop de caractères pour votre chaîne de caractères. Peut-être aussi avez-vous demandé 15 caractères alors qu'il n'en reste que 8 dans le fichier. Du coup, après avoir lu votre fichier, placé les caractères dans la chaîne et déplacé le pointeur, la fonction vous rend comme résultat le nombre de caractères qu'elle a effectivement réussi à lire.
Enfin, pour fermer le fichier (si vous êtes arrivé à la fin ou si vous avez toutes les informations que vous vouliez), utilisez la fonction close_in.
Exemple 1 : je veux lire les deux lignes du fichier précédent :
let canal_entree = open_in "C:/scores.txt";;
let ligne1 = input_line canal_entree;;
let ligne2 = input_line canal_entree;;
let ligne3 = input_line canal_entree;;
close_in canal_entree;;
Vous constaterez que ligne1 et ligne2 correspondent bien aux deux lignes du fichier. Quand à ligne3, Caml a déclenchée l'exception End_of_file puisque vous aviez déjà tout lu ! Exemple 2 : je veux stocker dans une chaîne, nommée initiales et de longueur 2, le premier caractère de chaque ligne :
let canal_entree = open_in "C:/scores.txt";;
let initiales = "00";; (* Je crée ma chaîne en mettant n'importe quoi dedans *)
let nombre1 = input canal_entree initiales 0 1;; (* je stocke en partant du premier caractère, d'où le 0, et je ne stocke qu'un caractère, d'où le 1. Caml donne comme résultat nombre1 = 1, il a bien lu 1 caractère *)
let ligne = input_line canal_entree;; (* je me moque du reste de cette ligne : cette instruction me sert à placer le pointeur au début de la ligne 2 pour lire ensuite son premier caractère *)
let nombre2 = input canal_entree initiales 1 1;;
close_in canal_entree;; (* j'ai ce que je voulais : je ferme mon canal même si je n'ai pas tout lu *)
Vous pouvez remarquer que la chaîne initiale vaut maintenant « HO ».
III) Opérer sur un fichier
Voyons maintenant des opérations d'un autre genre. D'abord, je souhaite pour une raison quelconque renommer mon fichier « scores.txt » en « nouveau.txt ». C'est très simple avec la fonction Sys.rename à laquelle je précise l'ancien nom et le nouveau : Sys.rename "C:/scores.txt" "C:/nouveau.txt";;. Notez qu'on peut en profiter pour changer le fichier de répertoire (en donnant un répertoire différent avec le nouveau nom) voire changer l'extension du fichier. Si le nouveau nom que vous donnez correspond à un fichier déjà existant, le comportement de Caml va dépendre de votre système, selon le cas le fichier sera écrasé ou vous obtiendrez une exception.
Enfin, je souhaite supprimer ce fichier (parce que le chapitre se termine et nous n'avons plus besoin de ce fichier-exemple). C'est également facile puisqu'il existe la fonction Sys.remove prenant comme unique argument le nom du fichier à supprimer : Sys.remove "C:/nouveau.txt";;. Attention ! Le fichier est vraiment supprimé : si vous utilisez Windows, n'espérez pas le retrouver dans la corbeille.
Remarquez que pour éviter les problèmes d'écrasement involontaires de fichiers ou pour ne pas avoir d'exceptions en tentant de lire ou supprimer un fichier inexistant, la fonction Sys.file_exists permet de savoir si un fichier existe à une adresse donnée.
Remarque : si vous n'utilisez pas Windows, ces fonctions peuvent rencontrer des difficultés avec le CHMOD des fichiers. La fonction Unix.chmod permet de modifier les permissions d'un fichier. Je vous renvoie pour cela au manuel de référence de Caml.