Recherche…
Remarques
Le constructeur par défaut de std::istream_iterator
construit un itérateur qui représente la fin du flux. Ainsi, std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(), ....
signifie copier de la position actuelle dans ifs
à la fin.
Flux de chaînes
std::ostringstream
est une classe dont les objets ressemblent à un flux de sortie (vous pouvez leur écrire via un operator<<
), mais stocke réellement les résultats de l'écriture et les fournit sous la forme d'un flux.
Considérez le code court suivant:
#include <sstream>
#include <string>
using namespace std;
int main()
{
ostringstream ss;
ss << "the answer to everything is " << 42;
const string result = ss.str();
}
La ligne
ostringstream ss;
crée un tel objet. Cet objet est d'abord manipulé comme un flux régulier:
ss << "the answer to everything is " << 42;
Après cela, le flux résultant peut être obtenu comme ceci:
const string result = ss.str();
(le result
la chaîne sera égal à "the answer to everything is 42"
).
Ceci est surtout utile lorsque nous avons une classe pour laquelle la sérialisation du flux a été définie et pour laquelle nous voulons une forme de chaîne. Par exemple, supposons que nous ayons une classe
class foo
{
// All sort of stuff here.
};
ostream &operator<<(ostream &os, const foo &f);
Pour obtenir la représentation sous forme de chaîne d'un objet foo
,
foo f;
nous pourrions utiliser
ostringstream ss;
ss << f;
const string result = ss.str();
Le result
contient alors la représentation sous forme de chaîne de l'objet foo
.
Lire un fichier jusqu'à la fin
Lecture d'un fichier texte ligne par ligne
Une manière appropriée de lire un fichier texte ligne par ligne jusqu'à la fin n'est généralement pas claire dans la documentation ifstream
. Considérons quelques erreurs courantes commises par des programmeurs C ++ débutants, et une manière appropriée de lire le fichier.
Lignes sans caractères d'espacement
Par souci de simplicité, supposons que chaque ligne du fichier ne contient aucun symbole d'espacement.
ifstream
a l' operator bool()
, qui renvoie true lorsqu'un flux ne contient aucune erreur et est prêt à être lu. De plus, ifstream::operator >>
renvoie une référence au flux lui-même, nous pouvons donc lire et vérifier EOF (ainsi que les erreurs) sur une seule ligne avec une syntaxe très élégante:
std::ifstream ifs("1.txt");
std::string s;
while(ifs >> s) {
std::cout << s << std::endl;
}
Lignes avec des caractères d'espacement
ifstream::operator >>
lit le flux jusqu'à ce qu'un caractère d' ifstream::operator >>
apparaisse, le code ci-dessus imprimera les mots d'une ligne sur des lignes séparées. Pour tout lire jusqu'à la fin de la ligne, utilisez std::getline
au lieu de ifstream::operator >>
. getline
renvoie une référence au thread avec lequel il a travaillé, donc la même syntaxe est disponible:
while(std::getline(ifs, s)) {
std::cout << s << std::endl;
}
De toute évidence, std::getline
devrait également être utilisé pour lire un fichier d'une seule ligne jusqu'à la fin.
Lecture d'un fichier dans un tampon à la fois
Enfin, lisons le fichier du début à la fin sans arrêter aucun caractère, y compris les espaces blancs et les nouvelles lignes. Si nous savons que la taille exacte du fichier ou la limite supérieure de la longueur est acceptable, nous pouvons redimensionner la chaîne et lire ensuite:
s.resize(100);
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
s.begin());
Sinon, il faut insérer chaque caractère à la fin de la chaîne, donc std::back_inserter
est ce dont nous avons besoin:
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::back_inserter(s));
Alternativement, il est possible d'initialiser une collection avec des données de flux, en utilisant un constructeur avec des arguments de plage d'itérateurs:
std::vector v(std::istreambuf_iterator<char>(ifs),
std::istreambuf_iterator<char>());
Notez que ces exemples sont également applicables si ifs
est ouvert en tant que fichier binaire:
std::ifstream ifs("1.txt", std::ios::binary);
Copier des flux
Un fichier peut être copié dans un autre fichier avec des flux et des itérateurs:
std::ofstream ofs("out.file");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::ostream_iterator<char>(ofs));
ofs.close();
ou redirigé vers un autre type de flux avec une interface compatible. Par exemple flux de réseau Boost.Asio:
boost::asio::ip::tcp::iostream stream;
stream.connect("example.com", "http");
std::copy(std::istreambuf_iterator<char>(ifs), std::istreambuf_iterator<char>(),
std::ostream_iterator<char>(stream));
stream.close();
Tableaux
Comme les itérateurs peuvent être considérés comme une généralisation de pointeurs, les conteneurs STL dans les exemples ci-dessus peuvent être remplacés par des tableaux natifs. Voici comment analyser les nombres dans un tableau:
int arr[100];
std::copy(std::istream_iterator<char>(ifs), std::istream_iterator<char>(), arr);
Méfiez-vous du débordement de la mémoire tampon, car les tableaux ne peuvent pas être redimensionnés à la volée après leur attribution. Par exemple, si le code ci-dessus est alimenté par un fichier contenant plus de 100 nombres entiers, il essaiera d'écrire en dehors du tableau et aura un comportement indéfini.
Impression de collections avec iostream
Impression de base
std::ostream_iterator
permet d'imprimer le contenu d'un conteneur STL sur n'importe quel flux de sortie sans boucles explicites. Le second argument du constructeur std::ostream_iterator
définit le délimiteur. Par exemple, le code suivant:
std::vector<int> v = {1,2,3,4};
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ! "));
imprimera
1 ! 2 ! 3 ! 4 !
Type de distribution implicite
std::ostream_iterator
permet de transtyper implicitement le type de contenu du conteneur. Par exemple, accordons std::cout
pour imprimer des valeurs à virgule flottante avec 3 chiffres après le point décimal:
std::cout << std::setprecision(3);
std::fixed(std::cout);
et instancier std::ostream_iterator
avec float
, alors que les valeurs contenues restent int
:
std::vector<int> v = {1,2,3,4};
std::copy(v.begin(), v.end(), std::ostream_iterator<float>(std::cout, " ! "));
donc le code ci-dessus donne
1.000 ! 2.000 ! 3.000 ! 4.000 !
malgré std::vector
tient int
s.
Génération et transformation
std::generate
, std::generate_n
et std::transform
fournissent un outil très puissant pour la manipulation des données à la volée. Par exemple, avoir un vecteur:
std::vector<int> v = {1,2,3,4,8,16};
nous pouvons facilement imprimer la valeur booléenne de l'instruction "x is even" pour chaque élément:
std::boolalpha(std::cout); // print booleans alphabetically
std::transform(v.begin(), v.end(), std::ostream_iterator<bool>(std::cout, " "),
[](int val) {
return (val % 2) == 0;
});
ou imprimer l'élément carré:
std::transform(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "),
[](int val) {
return val * val;
});
Impression de N nombres séparés par des espaces:
const int N = 10;
std::generate_n(std::ostream_iterator<int>(std::cout, " "), N, std::rand);
Tableaux
Comme dans la section sur la lecture des fichiers texte, presque toutes ces considérations peuvent être appliquées aux tableaux natifs. Par exemple, imprimons les valeurs au carré d'un tableau natif:
int v[] = {1,2,3,4,8,16};
std::transform(v, std::end(v), std::ostream_iterator<int>(std::cout, " "),
[](int val) {
return val * val;
});
Fichiers d'analyse
Analyse des fichiers dans des conteneurs STL
istream_iterator
s sont très utiles pour lire des séquences de nombres ou d'autres données analysables dans des conteneurs STL sans boucles explicites dans le code.
En utilisant une taille de conteneur explicite:
std::vector<int> v(100);
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
v.begin());
ou en insérant un itérateur:
std::vector<int> v;
std::copy(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
std::back_inserter(v));
Notez que les nombres dans le fichier d'entrée peuvent être divisés par un nombre quelconque de caractères d'espacement et de nouvelles lignes.
Analyse de tables de texte hétérogènes
Au istream::operator>>
mesure que istream::operator>>
lit le texte jusqu'à un symbole d' istream::operator>>
, il peut être utilisé dans une condition while
pour analyser des tables de données complexes. Par exemple, si nous avons un fichier avec deux nombres réels suivis d'une chaîne (sans espaces) sur chaque ligne:
1.12 3.14 foo
2.1 2.2 barr
il peut être analysé comme ceci:
std::string s;
double a, b;
while(ifs >> a >> b >> s) {
std::cout << a << " " << b << " " << s << std::endl;
}
Transformation
Toute fonction de manipulation de plage peut être utilisée avec les plages std::istream_iterator
. L'un d'eux est std::transform
, qui permet de traiter des données à la volée. Par exemple, lisons les valeurs entières, multiplions-les par 3.14 et stockez le résultat dans un conteneur à virgule flottante:
std::vector<double> v(100);
std::transform(std::istream_iterator<int>(ifs), std::istream_iterator<int>(),
v.begin(),
[](int val) {
return val * 3.14;
});