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 :
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 }