Test de rattrapage n° 2 de système
(10 mars 2001 – durée : 2h)
© D. Mathieu
mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique
Créé le 09/03/2001 -
Dernière mise à jour : 09/03/2001

Tout document autorisé
Fichier séquentiel indexé
Considérons qu'un fichier texte est composé
de lignes de tailles quelconques, chaque ligne étant terminée
par le caractère '\n'. On souhaiterait créer une
classe CRandomAccess permettant de gérer ce fichier, telle
qu'elle permette l'utilisation suivante :
{
CRandomAccess Fich ("NomDeFichier");
// ...
string StrLue = Fich [i];
// {1}
// ...
Fich [j] = string ("Nouvelle ligne");
// {2}
// ...
} |
La ligne {1} montre qu'il faudrait
surcharger l'opérateur [] pour qu'il puisse extraire la
ième ligne du fichier et la renvoyer sous forme d'un
objet de la classe standard string.
La ligne {2} montre qu'il faudrait
surcharger l'opérateur [] pour qu'il puisse écrire
un objet de la classe standard string dans la jème
ligne du fichier.
La surcharge de l'opérateur [] entrant
plus dans le cadre d'un test de C++ que de système, on se contentera
ici de pouvoir en faire l'utilisation suivante, moins élégante
:
{
CRandomAccess Fich ("NomDeFichier");
// ...
string StrLue = Fich.Get (i);
// {1}
// ...
Fich.Set (j, string ("Nouvelle ligne"));
// {2}
// ...
} |
Le principe proposé est le suivant :
-
le constructeur ouvre en lecture/écriture le fichier dont le nom
lui est passé en paramètre,
-
il lit le fichier ligne/ligne jusqu'à la fin du fichier,
-
pour chaque ligne lue, il range dans un tableau :
-
la position du premier octet de la ligne dans le fichier (l'offset),
-
la longueur effective de la ligne.
Lorsque la fonction Get(i) est appelée,
elle utilise les informations de l'élément i du
tableau pour
-
se positionner dans le fichier grâce à la fonction système
lseek(),
et
-
lire la ligne,
puis construit la chaîne correspondante qu'elle renvoie. Comme en
C/C++, la première ligne a pour indice 0.
Lorsque la fonction Set(i, Str) est appelée,
elle utilise les informations de l'élément i du
tableau :
-
si la nouvelle ligne n'est pas plus longue que la ligne précédemment
stockée, la nouvelle ligne est écrite par dessus,
-
si la nouvelle ligne est plus longue que la ligne précédemment
stockée, la nouvelle ligne est écrite en fin de fichier.
Dans les deux cas, le tableau est mis à jour.
Le destructeur :
-
ouvre un fichier temporaire,
-
parcourt le fichier texte en parcourant la table élément
par élément, relisant et en recopiant les lignes l'une après
l'autre,
-
referme les fichiers et
-
renomme le fichier temporaire en lui donnant le nom du fichier initial.
Travail à réaliser :
La classe CRandomAccess doit posséder
:
-
la déclaration d'une structure (struct) SElem,
représentant un élément du tableau indiqué
plus haut, formée :
-
d'un déplacement (offset) dans le fichier,
-
d'une longueur (de ligne),
-
d'un constructeur par défaut, permettant d'initialiser les deux
données membres.
-
la chaîne m_NomFich contenant le nom du fichier,
-
le file descriptor m_fd du fichier,
-
le nombre de lignes m_NbreLignes du fichier,
-
un tableau m_Index de 100 éléments SElem.
-
les méthodes suivantes :
CRandomAccess (const string
& NomFich);
~CRandomAccess (void);
string Get (unsigned i) const;
void Set (const string & Str, unsigned i); |
Constructeur
Le constructeur :
ouvre le fichier source, ou le crée s'il n'existe pas,
remplit le tableau en lisant le fichier ligne par ligne. On rappelle que
:
-
un flux ifstream peut être associé à un file descriptor
en passant ce dernier au constructeur ifstream(),
-
on peut lire une chaîne entière dans un flux jusqu'à
un délimiteur grâce à la fonction :
getline (flux, chaine, delimiteur); |
Modifieur
La fonction Set() écrit la chaîne
passée en paramètre dans le fichier. Plusieurs cas doivent
être envisagés :
-
la chaîne passée en paramètre est vide. Hors sujet.
-
l'indice i est dans l'intervalle [0, m_NbreLignes[. Il
s'agit d'une substitution d'une ligne par une autre. La nouvelle ligne
est écrite à la place de l'ancienne si c'est possible, ou
en fin de fichier sinon. Dans ce cas, l'espace du fichier occupé
par l'ancienne ligne est perdu jusqu'à la réorganisation
finale du fichier (dans le destructeur). Attention : les lignes
dans le fichier sont des suites de caractères terminées
par '\n' (et non '\0'),
-
l'indice i est hors de l'intervalle (mais dans l'intervalle [0,
100[). On considère qu'il s'agit d'une extension du fichier,
la nouvelle ligne est ajoutée en fin de fichier et l'élément
i du tableau est mis à jour. Les lignes intermédiaires (entre
m_NbreLignes
et i-1) sont considérées comme vides, et ne sont pas physiquement
enregistrées dans le fichier (dans le tableau : offset =
0, longueur = 0),
-
l'indice i est hors de l'intervalle [0, 100[. Il s'agit
d'une erreur qui devrait être traitée, par exemple par une
exception. Hors sujet.
Accesseur
La fonction Get() lit la ligne i dans
le fichier et renvoie la chaîne string corespondante. Plusieurs
cas doivent être envisagés :
-
l'indice i est hors de l'intervalle [0, m_NbreLignes[.
Il s'agit d'une erreur qui devrait être traitée, par exemple
par une exception. Hors sujet.
-
la ligne est "fictive" (dans le tableau : offset = 0, longueur =
0). La chaîne renvoyée est vide.
-
la ligne est réellement enregistrée dans le fichier, mais
vide. La chaîne renvoyée est vide.
-
l'indice i est dans l'intervalle [0, m_NbreLignes[. La
ligne est lue dans le fichier puis la chaîne string correspondante
est renvoyée.
Destructeur
Le destructeur :
-
crée un fichier temporaire,
-
y recopie ligne par ligne le fichier source, en parcourant séquentiellement
le tableau jusqu'au nombre de lignes, y compris les lignes vierges,
-
ferme les deux fichiers et renomme le fichier temporaire dans le fichier
source.
Deux fonctions peuvent être utilisées pour
cela
#include <cstdio>
char *tempnam (const char *rep, const char *préfixe);
// fonction standard du C
int rename(const char *oldpath, const char *newpath); // fonction
système |
La fonction tempnam() renvoie un pointeur
vers une chaîne de caractères allouée dynamiquement
par la fonction, et qui contient un nom de fichier unique situé
dans le répertoire rep (si rep != 0), et préfixé
d'au maximum 5 caractères de la chaîne prefixe (si
prefixe
!= 0). Une saine utilisation nécessiterait de libérer
ensuite l'espace mémoire, par appel de la fonction free()
: hors sujet.
La fonction système rename() renomme
le fichier oldpath en newpath. On supposera que son wrapper
Rename() a été ajouté dans l'espace de noms
nsSysteme.
Corrigé :
RandomAccess.cxx
© D. Mathieu mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique