| auteurs : LFE, Aurélien Regat-Barrel |
En plus de faire un retour à la ligne en ajoutant un caractère '\n', std::endl purge le buffer de sortie et force ainsi son écriture en appelant ostream::flush() (cela a le même fonctionnement que la fonction fflush() du C).
Les deux lignes de code suivantes sont donc équivalentes :
std:: cout < < " coucou " < < std:: endl;
std:: cout < < " coucou\n " < < std:: flush;
|
Il faut donc être prudent avec son utilisation, notamment avec les fichiers, car une opération de flush n'est pas gratuite. Son utilisation fréquente peut même sérieusement grever les performances en annulant tous les bénéfices d'une écriture bufferisée.
|
| auteurs : Laurent Gomila, Luc Hermitte |
L'opérateur >> de saisie d'une string permet de saisir des mots et donc considère les espaces comme des séparateurs.
Pour saisir une chaîne entière contenant des espaces, il faut récupérer l'intégralité de la ligne au moyen de la fonction libre std::getline() définie dans <string> (et non pas avec la fonction membre cin.getline() qui opère sur des char*).
# include <iostream>
# include <string>
std:: string chaine;
std:: getline ( std:: cin, chaine );
|
A noter que si une variable a été extraite à l'aide de l'opérateur >> avant l'appel à std::getline, le caractère de fin de ligne peut subsister dans
le flux d'entrée ; il peut donc être nécessaire de vider celui-ci avant d'extraire une nouvelle ligne (voir Comment purger le buffer clavier ?).
Remarque : l'implémentation de la fonction getline de la bibliothèque standard fournie par Microsoft avec Visual C++ 6.0
comporte un bug ; getline lit un caractère supplémentaire après la rencontre du délimiteur. Se référer au
support Microsoft pour la correction de ce bug.
|
| auteur : Laurent Gomila | # include <string>
# include <sstream>
int x = 127 ;
std:: ostringstream oss;
oss < < std:: hex < < x;
std:: string Hex = oss.str ();
|
On peut de la même manière obtenir une représentation décimale (std::dec), ou octale (std::oct).
|
| auteur : Laurent Gomila | # include <string>
# include <sstream>
std:: string Hex = " 7F " ;
int x;
std:: istringstream iss ( Hex );
iss > > std:: hex > > x;
|
On peut de la même manière convertir une représentation décimale (std::dec), ou octale (std::oct).
|
| auteur : Laurent Gomila |
Il n'existe aucun manipulateur pour la base 2 (binaire), mais on pourra y arriver au moyen de la classe std::bitset<>
# include <bitset>
# include <string>
# include <sstream>
int main ()
{
std:: bitset< 8 > b1 ( std:: string ( " 01001011 " ) );
unsigned long x = b1.to_ulong ();
std:: bitset< 8 > b2 ( 75 );
std:: string s1 = b2.to_string<
char ,
std:: char_traits< char > ,
std:: allocator< char > > ();
std:: ostringstream oss;
oss < < b2;
std:: string s2 = oss.str ();
}
|
|
| auteur : Laurent Gomila |
Il est possible, comme cela se faisait avec printf, de formater les informations envoyées sur un flux en utilisant ce que l'on appelle
les manipulateurs de flux. Voici un petit topo (non exhaustif) des manipulateurs les plus utilisés :
Manipulateur |
Persistant |
Effet |
Manipulateurs généraux
|
flush |
non |
Force le vidage du buffer du flux et son affichage |
endl |
non |
Envoie une fin de ligne ('\n') ainsi qu'un flush |
setw(n) |
non |
Spécifie que la prochaine sortie s'effectuera sur n caractères |
setfill(c) |
oui |
Indique le caractère de remplissage pour setw |
left |
non |
Aligne la sortie à gauche lors de l'utilisation de setw |
right |
non |
Aligne la sortie à droite lors de l'utilisation de setw (comportement par défaut) |
Formatage des nombres entiers
|
dec |
oui |
Injecte / extrait les nombres sous forme décimale |
oct |
oui |
Injecte / extrait les nombres sous forme octale |
hex |
oui |
Injecte / extrait les nombres sous forme hexadécimale |
uppercase |
oui |
Affiche les lettres de la représentation hexadécimale en majuscule |
nouppercase |
oui |
Affiche les lettres de la représentation hexadécimale en minuscule (annule l'effet d'uppercase) |
Formatage des nombres flottants
|
setprecision(n) |
oui |
Spécifie le nombre de chiffres après la virgule affichés pour les nombres flottants non entiers (6 par défaut) |
fixed |
oui |
Affiche les nombres flottants en notation décimale (opposé de scientific) |
scientific |
oui |
Affiche les nombres flottants en notation scientifique (opposé de fixed) |
Formatage des booléens
|
boolalpha |
oui |
Affiche les booléens sous forme alphabétique ("true" et "false" au lieu de "0" et "1") |
noboolalpha |
oui |
Affiche les booléens sous forme de chiffre (annule l'effet de boolalpha) |
Exemple
# include <iomanip>
# include <iostream>
using namespace std;
cout < < setfill (' ' )
< < setw (15 ) < < left < < " Colonne 1 "
< < setw (10 ) < < left < < " Colonne 2 "
< < endl;
cout < < setprecision (2 ) < < fixed
< < setw (15 ) < < left < < 158 .82589
< < setw (10 ) < < left < < 456 .10288
< < endl;
cout < < hex < < uppercase
< < setw (15 ) < < left < < 255
< < setw (10 ) < < left < < 128
< < endl;
cout < < boolalpha
< < setw (15 ) < < left < < true
< < setw (10 ) < < left < < false
< < endl;
|
A noter qu'il existe dans boost une bibliothèque de formatage style printf, mais plus évoluée et tirant partie des avantages offerts par
le C++ (voir boost::format).
|
| auteur : Laurent Gomila |
Pour injecter un objet de type quelconque dans un flux de sortie, il suffit de définir un opérateur << prenant en
paramètre le flux, ainsi que l'objet à injecter.
# include <ostream>
# include <string>
class MaClasse
{
friend std:: ostream& operator < < (std:: ostream& , const MaClasse& );
private :
int Integer;
std:: string String;
} ;
std:: ostream& operator < < (std:: ostream& Stream, const MaClasse& Obj)
{
Stream < < Obj.Integer < < " " < < Obj.String;
return Stream;
}
MaClasse Obj;
std:: cout < < " Ma classe : " < < Obj < < std:: endl;
|
Notez qu'ici notre surcharge d'opérateur est déclarée amie de la classe, car elle a besoin d'accéder aux données privées de
celle-ci. Cependant ce n'est pas obligatoire, notamment si les accesseurs adéquats ont été prévus dans la classe.
Le fonctionnement est le même pour l'extraction de valeurs à partir d'un flux d'entrée, via l'opérateur >> :
# include <istream>
# include <string>
class MaClasse
{
friend std:: istream& operator > > (std:: istream& , MaClasse& );
private :
int Integer;
std:: string String;
} ;
std:: istream& operator > > (std:: istream& Stream, MaClasse& Obj)
{
Stream > > Obj.Integer > > Obj.String;
return Stream;
}
MaClasse Obj;
std:: cin > > Obj;
|
Le fait de prendre en paramètre un ostream ou un istream permettra d'utiliser nos surcharges avec n'importe
quel type de flux (stringstream, fstream, ...) puisque ceux-ci dérivent tous des classes ostream
(pour les flux d'entrée) et istream (pour les flux de sortie).
Notez bien que ce genre de surcharge ne peut pas être membre de la classe, car celà impliquerait que l'opérande gauche
soit l'objet et non le flux.
|
| auteur : Laurent Gomila |
Habituellement, pour pouvoir envoyer un objet sur un flux on surcharge l'opérateur << entre un ostream et le type de notre objet. Cependant, cette fonction ne peut être membre et de ce fait elle ne peut pas non plus être virtuelle. Par contre rien ne l'empêche d'appeler une fonction membre virtuelle de l'objet passé en paramètre. Ainsi, la manière habituelle de surcharger l'opérateur << pour une hiérarchie de classes est la suivante :
# include <iostream>
class Base
{
friend std:: ostream& operator < < (std:: ostream& O, const Base& B);
virtual void Print (std:: ostream& O) const
{
O < < " Je suis une base " ;
}
} ;
class Derivee : public Base
{
virtual void Print (std:: ostream& O) const
{
O < < " Je suis une derivee " ;
}
} ;
std:: ostream& operator < < (std:: ostream& O, const Base& B)
{
B.Print (O);
return O;
}
Base* B1 = new Base,
Base* B2 = new Derivee;
std:: cout < < * B1 < < std:: endl;
std:: cout < < * B2 < < std:: endl;
|
|
| auteur : Laurent Gomila |
La fonction membre ostringstream::str a deux surcharges : une sans paramètre qui renvoie le contenu du flux sous forme de string, et une autre qui accepte une chaîne en paramètre pour réinitialiser le contenu du flux. Pour vider le flux il suffit donc d'appeler cette fonction avec comme paramètre la chaîne vide.
# include <sstream>
# include <string>
std:: ostringstream oss;
std:: string Fichier[10 ];
for (int i = 0 ; i < 10 ; + + i)
{
oss.str (" " );
oss < < " Image_ " < < i < < " .bmp " ;
Fichier[i] = oss.str ();
}
|
Note : on est souvent tenté d'utiliser la fonction clear, mais celle-ci n'a rien à voir : elle remet à zéro les bits d'erreur du flux.
|
| auteurs : Laurent Gomila, Aurélien Regat-Barrel |
# include <algorithm>
# include <iomanip>
# include <sstream>
# include <string>
std:: string array_to_string (const unsigned char * buffer, std:: size_t size)
{
std:: ostringstream oss;
oss < < std:: hex < < std:: uppercase;
std:: copy (buffer, buffer + size, std:: ostream_iterator< int > (oss, " " ));
return oss.str ();
}
|
On utilise ici std::copy et std::ostream_iterator<int> pour envoyer les éléments du buffer un à un au flux,
sous forme d'entiers. Le flux étant en mode hexadécimal, ceux-ci seront chacun correctement convertis. Le manipulateur std::uppercase
sert quant à lui à obtenir une représentation des lettres hexadécimales en majuscule.
Si l'on souhaite personnaliser la représentation hexadécimale, on peut développer la boucle de copie comme ceci :
# include <algorithm>
# include <iomanip>
# include <sstream>
# include <string>
std:: string array_to_string (const unsigned char * buffer, std:: size_t size)
{
std:: ostringstream oss;
oss < < std:: hex < < std:: uppercase;
oss < < std:: setfill (' 0 ' );
for (std:: size_t i = 0 ; i < size; + + i)
{
if (i ! = 0 )
oss < < ' ' ;
oss < < " 0x " < < std:: setw (2 ) < < static_cast < int > (buffer[i]);
}
return oss.str ();
}
int main ()
{
const std:: size_t size = 5 ;
const unsigned char data[size] = { 0x0C , 0x10 , 0xFF , 0x00 , 0xDA } ;
std:: cout < < array_to_string (data, size) < < ' \n ' ;
}
|
Le code ci-dessus produit le résultat suivant :
|
| auteur : Laurent Gomila |
Afin de rediriger l'entrée ou l'une des sorties standard (cin, cout, cerr ou clog) vers un
autre flux, il suffit de réaffecter leur streambuf (le streambuf est l'objet qui fait le lien entre le flux
et ce vers quoi il lit / écrit).
Afin d'accéder au streambuf d'un flux, il faut utiliser la fonction membre rdbuf (ses 2 surcharges pour être
précis) :
std:: streambuf* std:: ios:: rdbuf ();
void std:: ios:: rdbuf (std:: streambuf* );
|
Ainsi, pour rediriger cout vers un fichier par exemple, il suffira de procéder ainsi :
# include <fstream>
# include <iostream>
int main ()
{
std:: cout < < " Affichage sur la console " < < std:: endl;
std:: ofstream Out (" Toto.txt " );
std:: streambuf* OldBuf = std:: cout.rdbuf (Out.rdbuf ());
std:: cout < < " Affichage dans Toto.txt " < < std:: endl;
std:: cout.rdbuf (OldBuf);
return 0 ;
}
|
De cette manière vous n'êtes pas limité, cette méthode permet de rediriger n'importe quel flux vers n'importe quel autre.
A noter également que si vous ne souhaitez effectuer une telle redirection que ponctuellement et pour toute la durée de
l'exécution, vous pouvez plus simplement utiliser une redirection au niveau de votre invite de commande (avec une syntaxe
dépendante de l'OS) :
>MonProgramme.exe > Toto.txt
|
|
| auteur : Laurent Gomila |
La STL propose deux classes pour itérer sur les flux : istream_iterator et ostream_iterator
(définies dans l'en-tête <iterator>).
std:: vector< int > Tab;
Tab.push_back (1 );
Tab.push_back (3 );
Tab.push_back (6 );
std:: copy (Tab.begin (), Tab.end (), std:: ostream_iterator< int > (std:: cout, " \n " ));
|
Dans cet exemple, l'itérateur va parcourir les éléments du tableau un à un et utiliser l'opérateur << pour les
injecter dans le flux passé en paramètre, en l'occurrence ici std::cout.
Le second paramètre de l'itérateur est simplement le délimiteur qui sera placé entre chaque élément.
Vous pouvez également utiliser les stream_iterator avec des classes quelconques, pourvu que leur opérateur <<
soit correctement défini.
Cela marche aussi dans l'autre sens : avec un std::istream_iterator vous pourrez extraire des valeurs / objets d'un
flux les unes après les autres, et les placer par exemple dans un tableau.
std:: ifstream File (" Data.txt " );
std:: vector< MaClasse> Tab1;
std:: copy (istream_iterator< MaClasse> (File), istream_iterator< MaClasse> (), std:: back_inserter (Tab1));
std:: vector< MaClasse> Tab2 (istream_iterator< MaClasse> (File), (istream_iterator< MaClasse> ()));
|
Un istream_iterator construit par défaut (comme le second ici) représentera toujours la fin du flux.
La paire de parenthèses a priori inutile autour du second istream_iterator est nécessaire : sans cela le compilateur
croirait à une déclaration de fonction. Pour éviter cela vous pouvez également déclarer les variables correspondant aux
itérateurs en dehors de l'appel :
istream_iterator< MaClasse> Begin (File);
istream_iterator< MaClasse> End;
std:: vector< MaClasse> Tab2 (Begin, End);
|
Pour les traitements sur des flux binaires (ie. ouvertes avec l'option binary), il vaudra mieux utiliser
std::istreambuf_iterator, qui n'appliquera pas de traitement spécial aux caractères spéciaux.
|
lien : [Exemple] Comment manipuler un tableau de string ?
|
Consultez les autres F.A.Q.
|
|