Les flux d’entrées-sorties

Dans ce post, je parlerais principalement des classes ostream, istream et iostream en C++, qui permettent de manipuler les flux entrée/sortie.

I-Présentation générale

Définition d’un flux : Un flux est un transfert d’informations, partant d’un émetteur, jusqu’à un récepteur, qui consomme ces informations (ce flux).

Les 3 classes ci-dessous (voir tableau « Schéma classes de flux ») héritent de la classe mère ios, et il faut savoir que les fichiers en C++ sont considérés comme des flux, et que à l’exécution d’un programme, plusieurs instances de classes sont créées automatiquement : ( les instructions utilisées pour manipuler certains flux doivent d’ailleurs vous être familières ^^)

extern istream_withassign cin;  // Entrée par défaut (assignée au clavier)
extern ostream_withassign cout;  // Sortie par défaut (assigné à la console)
extern ostream_withassign cer;  // Sortie par défaut des erreurs (idem)
extern ostream_withassign clog;  // Sortie par défaut des journaux (idem)

Voici un petit schéma récapitulatif des liens entre ces classes :

classes de flux

Schéma classes de flux

Afin de définir si le flux est entrant ou sortant, les opérateur >> et << sont surchargés ( redéfinis), de façon à simplifier l’utilisation des 4 instances.

II-Formatage des flux

1 ) Les manipulateurs

Afin de formater les flux E/S, il est possible d’utiliser des « manipulateurs », qui sont en résumé des « arguments » supplémentaire que l’on fournit lors de l’utilisation du flux, pour effectuer toutes sortes de manipulations sur ceux-ci. Exemple, en C++, le manipulateur le plus connu est « endl », qui permet d’effectuer un saut de ligne.

cout << "hello world !" << endl;

Voici la liste des manipulateurs disponibles (selon les manipulateurs, il faudra des fois inclure <iomanip>) :

Syntaxe E ou S Description
dec E/S Numérotation décimale
hex E/S Numérotation hexadécimale
oct E/S Numérotation octale
endl S Saut de ligne
ends S Fin de chaîne
flush S Vide les tampons
ws S Ignore les espaces
skipws E Supprime les espaces
left S Alignement à gauche
right S Alignement à droite
showpoint S Force les décimales et le point à être affichés (même si elles sont à 0)
uppercase S Les lettres de A à F sont écrites en majuscules
scientific S Ecriture scientifique des nombres flottants
fixed S Ecriture en notation normale des nombres flottant

Source : Michel Heck

2 ) Pseudos-fonctions de manipulation

Il existe des pseudos-fonctions qui vont pouvoir être utilisées comme des filtres (implémentés dans iomanip.h) :

setiosflags(long indicateur) // Valide les indicateurs
resetiosflags(long indicateur) // Reset les indicateurs
setbase(int base) // Définit la base des variables numériques (8, 10 ou 16)
setfill(char c) // Définit le caractère de remplissage
setprecision(int precision) // Définit la précision décimale
setw(int largeur) // Définit la largeur de sortie
Exemple :
float pi = 3.14159
cout << setprecision(2) << setiosflag(ios::fixed) << pi << endl;

3 ) Méthodes de la classe ios

On retrouve deux méthodes qui permettent de valider ou d’invalider des indicateurs qui spécifient le type de formatage, ces deux méthodes sont respectivement

setf et unsetf. Ces méthodes peuvent prendre en paramètre les indicateurs (« flags ») suivants :

Indicateurs Description
skipws Ignore les caractères d’espacement (Entrée)
left Aligne le texte à gauche (Sortie)
right Aligne le texte à droite (Sortie)
internal Insère un caractère de remplissage entre le signe et le nombre
dec Conversion en décimal (Sortie)
oct Conversion en octal (Sortie)
hex Conversion en hexadécimal (Sortie)
showbase Le nombre est précédé d’un indicateur de base
showpoint Force l’affichage des virgules pour les flottant (Sortie)
uppercase Les lettres d’une expression héxadécimale sortent en majuscules
showpos Insère un « + »devant les entiers positifs (Sortie)
scientific Les nombres sont affichés en notation scientifique (Sortie)
fixed Verrouille les flottant en virgule fixe
unitbuf Vide les tampons après écriture (Sortie)
stdio Vide les tampons stderr et stdout après l’écriture (Sortie)

Source : Michel Heck

* le mot (Sortie) indique que le flag s’applique sur du flux sortant.

Donnons ici un exemple de code :

int nombre = 0xFF;
cout << "Nombre décimal : "<< nombre << endl; // 255
cout.setf(ios::hex);
cout << "Nombre héxadécimal : "<< nombre << endl; // ff

4 ) Les autres méthodes de la classe :

a) Méthode flags :

Il y a deux façons d’utiliser cette méthode :

  • Sans argument : permet d’obtenir la valeur du flag correspondant
  • Avec argument : permet de définir une nouvelle valeur pour le flag correspondant

Exemple :

cout.flags(cout.flags()|ios::showbase); // demande l'affichage du symbole de base

b) Méthode width :

La méthode width(int largeur) permet de forcer la longueur sur laquelle va s’écrire le texte.

Exemple :

cout.width(8);
cout << 'o' << 11 << 'o';  // affichera : "        o11o"

c) Méthode precision :

precision(int nb_decimals) fixe le nombre de chiffres affichés après la virgule d’un flottant :

Exemple :

float f = 2.3 / 2.8;
cout.precision(4);
cout << f << endl;  // affichera : 0.8214

d) Méthode fill :

La méthode fill(char brg) définit le caractère de remplissage, qui se trouve être espace par défaut.

 e) Méthode get :

Renvoie la valeur du caractère précédemment saisi, exemple :

char caracetere;
caractere = cin.get();

 f) Méthode getline :

Saisie d’une chaîne de caractère :

istream & getline(char _FAR * buffer, int nb_caracteres_max, char delimiteur="\n");
char * ma_chaine;
ma_chaine = new char[20];
cin.getline(ma_chaine,15);   // place "\0" au 15ème caractère lu

g) Méthode write :

Les méthodes put() ou write() écrivent respectivement un ou des caractères ( comme pour getline, ceux-ci doivent être préalablement définis dans les buffer comme ci-dessous) :

ostream & write(const char _FAR * buffer, int nb_caracteres);

 III-Les fichiers sur disque

1) Présentation

Il existe différents types de fichiers, les fichiers ASCII (à peu de choses près des fichiers texte), qui contiennent des nombres interprétés par leur code ASCII.

On trouve aussi des fichiers binaires, ( des fichiers exécutables ), composés de valeurs numériques, de texte, qui sont interprétés par la machine.

Dans ces fichiers, dépendamment  du système d’exploitation on retrouve en fin de ligne les caractères ‘\n’ pour l’univers Unix et le duo de caractère ‘\n’ ‘\r’ pour une plateforme Windows/DOS.

2) La classe ofstream : connexion d’un flux avec un fichier disque

La classe ofstream comporte plusieurs constructeurs :

  • ofstream(const char * nom, int mode, int prot); // (nom du fichier, mode d'ouverture (par exemple ios::out) et la permission (0 sous Windows)
  • ofstream(int descripteur_de_fichier);

Pour les modes d’ouvertures disponibles, voici la liste :

Mode d’ouverture Valeur Description
in 0x01 ouverture en lecture
out 0x02 ouverture en écrivant
ate 0x04 positionne à la fin du fichier
app 0x08 mode append : tout ajout en fin de fichier
trunc 0x10 vide le fichier s’il existe
nocreate 0x20 échec  en ouverture si fichier inexistant
noreplace 0x40 échec en ouverture si fichier déjà existant
binary 0x80 fichier binaire (ASCII par défaut)

Source : Michel Heck

Exemple :

#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <iostream>
using namespace std;
ofstream mFile("donnees.dat", ios::out, 0644); //0644 :  fonctionne avec le même principe que chmod sous Linux, voir article sur La programmation Bash
// Le fichier est créé si il n'éxiste pas

IV- Écriture dans un fichier

La méthode d’écriture dans un fichier avec ostream est certes, un peu obsolète, mais agit à très bas niveau, et est utilisée par de nombreuses librairies plus « haut niveau » qui écrivent dans des fichiers. C’est pourquoi son utilisation est en général à éviter, mais on comprend ainsi comment cela fonctionne, en étudiant les librairies natives du C++.

La classe ostream possède deux méthodes différentes permettant l’écriture d’un flux dans un fichier :

ostream & put(char c); // écrit c dans le fichier
ostream & write( const char* p_str, int nb); // écrit nb caractères de la chaine p_str dans le fichier

Rien ne vaut mieux que des petits exemples, alors allons-y :

#include <fstream.h>
int main(int argc, char * argv[])
{
char * my_string = "salutations à vous";  // chaine de 19 octets
ofstream fout(argv[1], ios::out, 0600); // 0600 : voir article La programmation Bash
cout << my_string;
fout.write(my_string, 11); // écrit 11 octets ( soit "salutations") dans le fichier
fout<< my_string // on écrit la totalité de my_string dans le fichier
char c =NULL;
while(c != 27)
{
     cin >> c; // flux clavier
     fout.put(c); /flux vers le fichier
}

 

 

Laisser un commentaire