Chapitre 6
Une famille de classes utiles : la bibliothèque IO-stream

L'utilisation de cette bibliothèque d'entrée-sortie offre l'avantage de gérer directement et sans risque d'erreur les types des variables ou objets entrés et sortis dans toutes les situations standard. C'est à comparer avec la bibliothèque d'entrée-sortie du C où le type des variables manipulées doit être indiqué dans la chaîne de format. Au contraire, C++ nomme les objets à l'endroit où ils doivent apparaître :

//bibliotheque du C
printf("la valeur du %d eme element est %f\n",i,x[i]);

//bibliotheque du C++
cout << "la valeur du " << i << "eme element est : " << x[i+1] << "\n";

On constate une plus grande lisibilité et un plus faible risque d'erreur :

C'est le compilateur qui, connaissant le type de i et x[i] insère leur valeur convenablement décodée dans le flux cout.

Ces manipulations sont gérés par des objets appartenant aux classes suivantes :

  1. les entrées (lecture) sont gérées par des objets de la classe istream, cette classe ayant les classes dérivées ifstream pour les fichiers et istrstream pour les chaînes de caractères.

  2. Les sorties (écriture) sont gérées par des objets de la classe ostream, cette classe ayant les classes dérivées ofstream pour les fichiers et ostrstream pour les chaînes de caractères.

Les deux classes istream et ostream dérivent de la classe de base ios qui contient les données et fonctions communes, tels que les flags de format.

Elles sont classes de base de la classe iostream, qui par ce double héritage permet de manipuler des flux dans les deux directions, en particulier les fichiers par la classe fstream qui en hérite.

1  Insertion et extraction

Les opérateurs d'insertion << et d'extraction >> permettent respectivement d'écrire et de lire une information dans (depuis) des objets ostream (istream) (ou des classes dérivées). Ce sont des surcharges dans les classes ostream et istream (définies pour tous les types courants) des opérateurs de décalage bien connus.

1.1  Insertion

Il pointe (graphiquement) sur l'objet ostream dans lequel l'information est insérée.

Si par exemple cette information est une chaîne de caractères, le prototype de la surcharge de l'opérateur d'insertion est :

ostream &ostream::operator <<(char const *text);

Il opère donc sur la chaîne pointée par text et retourne une référence ostream & sur l'objet de classe ostream qui l'a appelé.

L'opérateur est donc associatif, ce qui permet d'écrire des instructions telles que

cout << "bonjour " << "monsieur";

Dans ce cas :

cout << "bonjour "
est d'abord évalué, qui retourne une référence sur l'objet cout dans lequel la chaîne "bonjour" a été insérée

la même opération peut donc se répéter avec la deuxième chaîne :

cout << "monsieur" 
qui se trouve ainsi insérée dans cout à son tour.

Une variante de cette surcharge existe pour de nombreux types du membre de droite de l'opérateur, l'utilisateur peut en définir de nouveaux, spécifiques à son application.

Grâce à l'amitié, des nouvelles classes peuvent contenir une surcharge de << utilisable avec des objets ostream.

 

Remarque

Si l'opérande de droite de l'opérateur d'insertion est un pointeur sur caractère, c'est la chaîne pointée qui est insérée, ce qui correspond à ce qui est souhaité le plus souvent. Si on veut insérer la valeur du pointeur, il faut le convertir en void *.

char *tampon=newDup("Bonjour !");
cout << "Chaine pointee : " << tampon << "\n";
cout << "Valeur du pointeur : " << (void *)tampon << "\n";

Ces lignes donnent à l'exécution :

Chaine pointee : Bonjour !
Valeur du pointeur : 0x003000E0 

1.2  Extraction

Comme pour la fonction scanf(), les blancs sont ignorés par cet opérateur et indiquent la fin de la saisie d'une chaîne. On ne peut donc pas l'utiliser pour entrer une chaîne de caractères contenant des blancs, comme par exemple "rue de la paix".

Il reçoit une référence sur une variable dont on veut fixer la valeur et retourne la référence à l'objet auquel il appartient, ce qui assure son associativité.

cin >> x;
cin >> c >> y; 

2  Les quatre objets standard

Les objets standard sont :

  1. cin, objet de classe istream, d'où on peut extraire de l'information. Il est "connecté" au clavier.

  2. cout, objet de classe ostream, dans lequel on peut insérer de l'information. Il est "connecté" à l'écran.

  3. cerr, objet de classe ostream, dans lequel on peut insérer de l'information. Il est connecté à l'écran et ne possède pas de buffer.

  4. clog, comme cerr mais avec buffer.

3  Fichiers et chaînes de caractères

3.1  Chaînes ordinaires

Les chaînes ordinaires (tableau de caractères terminé par '\0') peuvent être traitées comme les fichiers si des objets istrstream ou ostrstream sont créés et leur sont associés.

Ces objets lisent des informations dans la mémoire ou écrivent des informations dans la mémoire, sous forme de caractères (octets).

Exemple, pour écrire une chaîne dans un bloc mémoire pointé par buffer :

char buffer[100];               //le bloc memoire

ostrstream memoire(buffer,100); //déclaration de l'objet
                                //constructeur avec parametres
                                //qui connecte l'objet à buffer

memoire << "Bonjour monsieur" << endl << ends;

cout << memoire.str();

Fonctions membres :

  1. istrstream::istrstream(const char *str [,int size]):

    constructeur pour un flux associé au bloc mémoire pointé par str et de dimension size. Si la dimension size n'est pas précisée, le bloc mémoire est traité comme une chaîne terminée par le caractère nul.

  2. ostrstream::ostrstream():

    constructeur pour un flux de sortie qui sera géré dynamiquement : la taille et l'adresse du bloc mémoire associé ne sont pas précisés.

  3. ostrstream::ostrstream(char *str, int size [, int mode]):

    constructeur pour un flux de sortie vers une chaîne définie par les arguments. Le mode optionnel peut être l'un des modes iostream. par défaut : ios::out.

  4. int ostrstream::pcount():

    retourne la longueur courante de la chaîne associée .

  5. char *ostrstream::str():

    retourne un pointeur sur la chaîne associée à l'objet. Cette fonction implique freeze() ci-dessous.

  6. void ostrstream::freeze ([int n]):

    Si n est non nul (par défaut), la chaîne associée à l'objet ne peut plus être modifiée dynamiquement. Elle ne sera pas réallouée (si plus d'espace est nécessaire), ni désallouée quand l'objet sera détruit.

  7. int ostrstream::frozen():

    Teste si la chaîne est gelée.

3. 2 Objets de la classe string

On a vu que les objets de la classe string permettent de manipuler des séquences quelconques de caractères, sans avoir à se soucier de la gestion de la mémoire qui est prise en charge par les fonctions membres de la classe de façon transparente. Il n'y a aucune contrainte sur la nature des caractères de la séquence. En particulier le caractère '\0' n'est pas utilisé pour marquer la fin de la chaîne, et peut figurer à n'importe quelle place dans la séquence.

Les objets des classes istringstream et ostringstream permettent de connecter le programme à ces objets de la classe string.

Le fichier d'en-tête <sstream> doit être inclus, et il faut se placer dans l'espace de nom std

ostringstream adresse;
adresse << "18" << " rue des pierres ";
string texte=sortie.str();
cout << texte << endl;

Donne à l'exécution l'affichage de la chaîne "18 rue des pierres".

4  Ouverture d'un flux

C'est la déclaration d'un objet istream (lecture) ou ostream (écriture) ou des classes dérivées selon la nature du flux. Plusieurs constructeurs existent:

4.1  En écriture

Fichiers

ofstream fichier_sortie("SORTIE.DAT"); 

Création de l'objet fichier_sortie associé au fichier physique SORTIE.DAT, qui est ouvert en écriture s'il existe ou créé en écriture s'il n'existe pas.

Les informations éventuellement présentes dans ce fichier seront écrasées.

ofstream fichier_sortie("SORTIE.DAT",ios::out);

Précise le mode d'ouverture par la valeur out d'une énumération de la classe ios

Même résultat que ci-dessus puisque c'est le mode par défaut pour les objets ofstream.

ofstream fichier_sortie("SORTIE.DAT",ios::app);

Ouverture en mode ajout à la fin du fichier (append).

La valeur retournée par ces constructeurs est nulle en cas d'échec.

Chaînes

ostrstream texte;

création d'un objet vide

ostrstream texte_sortie("Introduction");

création et initialisation par la chaîne passée en argument.

Ces chaînes grandissent dynamiquement au fur et à mesure que des informations y sont insérées.

Elles ne sont pas automatiquement terminées par le caractère de fin de chaîne.

4.2  En lecture

Fichiers

ifstream fichier_entree("ENTREE.DAT");

Le fichier ENTREE.DAT est ouvert en lecture et associé à l'objet fichier. Le fichier doit exister au préalable, sinon la création de l'objet fichier échoue (et la valeur 0 est retournée).

ifstream fichier_entree("ENTREE.DAT", ios::in);

même effet.

Chaînes

istrstream texte_entree("Conclusion");

4.3  Mode texte ou binaire

Le mode texte est le mode par défaut.

On peut préciser ce mode par ios::binary ou ios::text.

ofstream fichier_sortie("SORTIE.DAT",ios::app|ios::binary);

5  Lecture et écriture

5.1  Les opérateurs d'insertion et d'extraction

Insertion  

Les objets ouverts en écriture utilisent l'opérateur d'insertion << :

fichier_sortie << "Chapitre 1\n";

Extraction  

Les objets ouverts en lecture utilisent l'opérateur d'extraction >> qui en extrait l'information :

fichier_entree >> x >> y;

Il faut se souvenir que, par défaut, l'opérateur d'extraction saute les blancs qui lui indiquent la fin des champs :

si fichier_entree contient

56
23
a b
bonjour Monsieur

quel est l'effet du code suivant ?

int i,j;
char c, mot[10];

fichier_entree >> i >> j >> c >> c >> mot >> mot;

Lecture et écriture  

Si les deux opérations doivent être faites sur un fichier (une chaîne), un objet fstream (strstream) doit être créé.

fstream fichier("DATA.ES",ios::in|ios::out);

Les deux opérateurs d'extraction et d'insertion peuvent alors être utilisés sur cet objet.

5.2  Fonctions membres

Lecture  

  1. get(c) extrait un caractère et l'écrit dans la variable c

  2. get() : extrait un caractère et retourne sa valeur sous la forme d'un entier

  3. get(ptr,lon,delim) : extrait une séquence de caractères et l'écrit dans la mémoire pointée par ptr. La longueur de la séquence est lon, à moins que le caractère delim soit rencontré, auquel cas l'extraction s'arrête (delim n'est pas extrait), ou que le flux soit vidé avant l'un de ces deux événements.

    La séquence copiée en mémoire est terminée par '\0'.

    La valeur par défaut de delim est '\n'.

  4. getline(ptr,lon,delim) : comme get(ptr,lon,delim), mais delim est extrait.

    La valeur par défaut de delim est '\n'.

  5. read(char *,int) La fin de fichier est indiquée par la fonction membre eof. Cette fonction est intéressante en mode binaire.

  6. ignore(n,d) : extrait et ignore n caractères du flux, sauf si le caractère représenté par l'entier d est rencontré avant.

    Utile si le flux est coincé par un caractère indésirable.

    Valeur par défaut de d : EOF.

Écriture  

  1. put(c) : insère le caractère c dans le flux.

  2. write(ptr,n) : insère n caractères lus à partir de l'adresse donnée par ptr. Cette fonction est intéressante en mode binaire.

6  Formatage

On dispose pour cela de fonctions membres et de manipulateurs.

6.1  Manipulateurs

Il faut inclure iomanip.h pour les utiliser.

Ils sont définis dans la surcharge des opérateurs d'extraction et d'insertion et doivent être insérés dans une instruction d'extraction ou d'insertion. En voici quelques uns :

  1. setw(n) : fixe la largeur du champ à n caractères au moins.
    cout << setw(10) << i ;

  2. setprecision(n) : définit la précision de l'affichage des réels.

  3. setfill(n) : définit le caractère de remplissage du champ.

  4. setiosflag(l) permet d'activer le format défini par la valeur de l'entier long l (voir les flags de format)

  5. resetiosflag(l) : désactive le format

  6. flush : vide le flux (de sortie)

  7. endl : fin de ligne et vide le flux (de sortie)

  8. ends : fin de chaîne et vide le flux (de sortie)

6.2  Fonctions membres

Les mêmes résultats peuvent être obtenus en appelant des fonctions membres du flux manipulé :

  1. width(n) : fixe la largeur (minimale) du champ.

  2. precision(n) :

    cout.precision(4);
    cout << sqrt(2) << endl;
    cout.precision(6);
    cout << -sqrt(2) << endl;    

  3. fill(c) : caractère de remplissage.

  4. setf() et unsetf()

    positionnement des drapeaux de format.

    La fonction membre setf() définit le format d'affichage des nombres en activant des flags de la classe ios.

    Elle reçoit un ou deux arguments (des flags de la classe ios).

    La fonction membre unsetf() désactive au contraire des flags de la classe iosm

    Par exemple :

    cout.setf(ios::showbase); cout << 16 << ", " << hex 
    	<< 16 << ", " << oct << 16 << endl; 

    donne le résultat :

    16, 0x10, 020
    

    Pour l'affichage des réels :

    cout.setf(ios::fixed, ios::floatfield);
    cout << sqrt(200) << endl;
    cout.setf(ios::scientific, ios::floatfield);
    cout << sqrt(200) << endl;
    

    donne :

    14.142136
    1.414214e+01
    

    Également :

    ios::left ,ios::internal, ios::skipws,setf(ios::showbase) (affiche la base de numération des entiers), setf(ios::showpoint) pour afficher le point décimal des réels, setf(ios::dec, ios::basefield), setf(ios::hex, ios::basefield), setf(ios::oct, ios::basefield) : même effet que les manipulateurs dec, hex et oct

7  Positionnement dans le flux

Les fonctions membres seekg(), tellg() (pour les flux en lecture (g est pour getpointer) et seekp(), tellp() (pour les flux en écriture (putpointer) permettent de se positionner (seek) ou de donner la position (tell) dans un flux.

Par exemple :

seekg(long offset, seek_dir position = ios::beg);

seek_dir position peut être :

ios::beg : début du fichier

ios::end

ios::cur : position courante

8  Fermeture

Fonction membre close()