2.5- Les flux 1- Librairies de flux 2- Manipulation de cin 3- Lecture et écriture de fichiers 4- Les flux et les chaînes 5- Passage de paramètres en ligne de commande 6- Un exemple de récupération CSV Depuis le premier exemple développé dans le chapitre 1.1, on utilise la librairie iostream et notamment l'objet std::cout qui un flux vers l'invite de commandes Windows ou le shell Linux. Les objets flux gèrent l'interfaçage vers les différents éléments et peuvent être conçus de manière assez complexe. Par exemple, les flux vers les fichiers peuvent intégrer une mémoire tampon : pour éviter d'accéder constamment au disque dur (lent), le flux remplit une mémoire tampon au fur et à mesure des instructions d'écriture qui apparaissent dans le code. L'écriture sur le disque se fait alors en une seule fois, lorsque le tampon est plein (ou selon d'autres modalités). 1- Librairies de flux On a déjà utilisé la librairie iostream : celle ci contient quatre variables objet. Une variable de type istream (flux entrant) : cin et trois variables de types ostream (flux sortant) : cout, cerr, clog. Deux autres librairies de flux vont être évoquées dans ce cours. <fstream> est une librairie qui permet de manipuler des fichiers. <sstream> est une librairie qui permet de manipuler les string comme si elles étaient des flux. On peut trouver une visualisation plus complète des librairies de flux : http://www.cplusplus.com/reference/iostream/. Si on teste les flux cerr et clog par défaut, on constate qu'ils écrivent sur l'invite de commande à l'instar de cout. Noter qu'on peut
rediriger les flux, ce qui n'est pas évoqué ici. La redirection des flux consiste à orienter le flux cout (ou cerr ou clog) vers un fichier par exemple, et non plus vers l'invite de commandes. De manière générale, un stream ou flux est un objet qui transporte et formate des caractères. Deux grandes catégories : istream (un flux en entrée) et ostream (un flux de sortie). Pour les objets-flux d'entrée, on a ifstream (flux d'import depuis un fichier), istringstream (flux d'entrée depuis une chaîne de caractères). Pour les objets-flux de sortie, on a ofstream (flux d'export vers un fichier), ostringstream (flux de sortie vers une chaîne). Deux opérateurs sont utilisés : << qui est un opérateur d'insertion (on met les données dans un flux), >> qui est n opérateur d'extraction (on récupère des données depuis un flux). 2- Manipulation de cin cin a déjà été évoqué dans le chapitre 1.2. On développe un exemple proche de celui proposé en 1.2 : double x,y; std::cout << "Rentrer une premiere valeur\n"; std::cin >> x; std::cout << "Rentrer une seconde valeur\n"; std::cin >> y; std::cout << "x : " << x << "\ty : " << y << "\n"; Dans ce code, std::cin >> x envoie le contenu du flux dans x. L'utilisateur valide une saisie en utilisant la touche entrée. En plus des valeurs numériques, cin peut gérer les arguments de type char*. char prenomutilisateur[100]; cout << "Quel est votre prenom?\n"; cin >> prenomutilisateur; std::cout << "A ete saisi : " << prenomutilisateur << "\n"; cin rajoute automatiquement \0 à la fin de la chaîne qui a été saisie. Si l'utilisateur saisi "Jean", alors la chaîne enregistrée est : 'J' 'e' 'a' 'n' '\0' Attention aux erreurs si on ne prévoit pas un buffer de caractères de taille suffisante (un tableau de taille suffisante dans le code
qui précède) : dans ce cas, le buffer affectera une partie de la mémoire qui n'a pas été réservée à cet effet, avec les conséquences que l'on sait que cela peut avoir (détournement de mémoire destinée à un autre usage). L'objet cin, lorsqu'il traite une entrée, considère les espaces ' ' comme des retours à la ligne '\n'. Soit dans le cas du code précédent, si l'utilisateur saisi un prénom avec un espace au milieu, seul le premier prénom sera affiché. Un exemple d'exécution à partir du code précédent : C:\CPP>re Quel est votre prenom? Jean Charles A ete saisi : Jean On peut renseigner plusieurs variables à la fois : int main() char prenomutilisateur[100],nomutilisateur[100]; cout << "Donnez votre nom et votre prenom : \n"; cin >> nomutilisateur >> prenomutilisateur; int age,taille; cout << "Donnez votre age et votre taille en cm\n"; cin >> age >> taille; cout << "Vous etes : " << prenomutilisateur << " " << nomutilisateur << ", d'age " << age << "an, de taille "<< taille << "cm\n"; Généralement, l'utilisateur rentrera son nom et son prénom sur une même ligne avec un espace entre les deux, de fait, la machine considérera qu'il y a deux saisies et renseignera la chaîne nomutilisateur et la chaine prenomutilisateur. Ensuite, l'utilisateur va rentrer une taille, soit il met un espace, soit il valide par entrée, dans les deux cas, la machine attendra une seconde valeur pour poursuivre l'exécution. Pour lire l'ensemble des caractères en une seule fois, on utilise la fonction get : int main() char identite[100]; cout << "Donnez votre nom et votre prenom : \n"; cin.get(identite,99); cout << "identite\n";
Pour connaître le nombre des caractères contenus dans le flux : cin.gcount. 3- Lecture et écriture de fichiers Les classes ofstream et ifstream La classe ofstream permet d'écrire des données sur les fichiers, la classe ifstream permet de lire des données dans un fichier. Il est toujours important d'utiliser la fonction close() des objets ofstream ou ifstream qui sont créés, notamment pour libérer les ressources systèmes liées à ces objets et en particulier pour vider la mémoire tampon associée au flux sur le fichier. Un premier exemple : écrire dans un fichier Un exemple le plus simple possible pour créer et remplir un fichier texte : #include <fstream> //cette librairie est nécessaire pour la manipulation des fichiers, elle contient la classe ofstream using namespace std; // avec fstream, on est toujours dans la librairie standard et donc dans l'espace de nom std. int main() ofstream fluxfichier("c:\\cpp\\output\\test.txt"); // l'objet flux sur fichier est créé fluxfichier << "Les sanglots longs des violons de l'automne...\n"; fluxfichier.close(); //On ferme le flux sur fichier, le tampon est vidé. Un autre exemple, on va générer un certain nombre de valeurs aléatoires selon une gaussiennes pour fournir une série de nombres aléatoires et les stocker dans un fichier. #include <fstream> //cette librairie est nécessaire pour la manipulation des fichiers, elle contient la classe ofstream #include <iostream> #include <cstdlib> #include <ctime> #include <cmath> using namespace std;
double randomuniform() return rand()/(double)rand_max; void polarmarsaglia(double *x1,double *x2) double W,V1,V2; do double U1=randomUniform(); double U2=randomUniform(); V1=2*U1-1; V2=2*U2-1; W=V1*V1+V2*V2; while(w>1); /*Apres cette étape, V1 et V2 ont été tirés de manière uniforme sur [-1;1] / V2²+V1²<=1*/ double W_function=sqrt(-2*log(W)/W); *x1=v1*w_function; *x2=v2*w_function; int main() ofstream fluxfichier("c:\\cpp\\output\\test.txt"); // l'objet flux sur fichier est créé fluxfichier << "Le fichier contient une serie de nombres aleatoires generes selon une loi gaussienne\n"; double x1,x2; for(int i=0;i<1000000;i++) polarmarsaglia(&x1,&x2); fluxfichier << i << " " << x1 << " " << x2 << "\n"; fluxfichier.close(); //On ferme le flux sur fichier, le tampon est vidé. Dans le code qui précède, lors de la création de l'objet ofstream, la machine tente d'ouvrir le fichier c:\cpp\output\test.txt. Si ce fichier n'existe pas, il sera crée (à condition que le répertoire existe lui), si le fichier existe, il sera écrasé.
Un exemple de lecture depuis un fichier #include <fstream> //cette librairie est nécessaire pour la manipulation des fichiers, elle contient la classe ifstream #include <iostream> int main() std::ifstream lecturefichier("c:\\cpp\\output\\test.txt"); char ch; while(lecturefichier.get(ch)) std::cout << ch << "\n"; std::cout << "Fin de la lecture du fichier\n"; lecturefichier.close(); Noter qu'ici, comme on n'a pas mis using namespace std;, on travaille avec std::ifstream : le nom de la classe ifstream ne serait pas reconnu sinon. Ici, on utilise la fonction get(char) de ifstream qui pointe à tout moment sur un caractère. Au début, ifstream pointe sur le premier caractère. Au premier appel de get(ch), il affecte le premier caractère à ch puis pointe sur le deuxième caractère etc... Au final, get(char) renvoie faux. Modifier le comportement à l'ouverture des fichiers Pour l'instant, on utilise le constructeur ofstream qui prend une chaîne de caractères char* en entrée et crée le fichier qui correspond au chemin si celui n'existe pas, si ce fichier préexiste, il sera effacer. On peut également vouloir écrire à la fin du fichier sans effacer les données précédentes. Pour cela, on utilise des attributs que l'on passe en argument au constructeur : char *chemin="c:\\cpp\\output\\test.txt"; ofstream flux1(chemin,ios::app); // ajoute les données à la fin du fichier que l'on ouvre ofstream flux2(chemin,ios::noreplace); // le fichier sera ouvert si il n'existe pas déjà ofstream flux3(chemin,ios::nocreate); // le fichier sera ouvert seulement si il existe déjà
4- Les flux et les chaînes Istringstream : un flux d'input depuis une string A partir d'une string, on peut construire un flux isstringstream. A partir de ce flux, il est possible d'extraire des valeurs. Soit par exemple le code suivant : #include <sstream> #include <iostream> using namespace std; int main() string str("5 12.7\nParis"); istringstream in(str); int i;double d; char chaine1[10]; in >> i >> d >> chaine1; // recuperation des valeurs de la chaîne de caractères vers des variables std::cout << i << " " << d << " " << chaine1 << "\n"; Dans ce code, on crée un objet istringstream à partir d'une chaîne. Istringstream est une classe définie dans sstream. On peut ensuite extraire les valeurs depuis la chaîne. L'objet istringstream permet donc d'obtenir des valeurs numériques à partir des string. L'utilisation de ce type d'objet coexiste avec une autre méthode qui est celle issue du C et qui propose deux fonctions atoi() et atof() qui permettent d'obenir des int ou des float à partir de chaînes. On préfera bien sûr utiliser la méthode reposant sur les objets istringstream. ostringstream Il est possible d'utiliser ostringstream qui est un flux de sortie : on y adresse du contenu puis on extraie une chaîne à partir du ostringstream : #include <sstream> #include <iostream>
using namespace std; int main() int n=12000; string str("place de la Republique"); ostringstream out; out << "A la date du 12 mai 1995, se retrouveaient " << n << " personnes " << str; string str2=out.str(); std::cout << str2; Ce type de démarche permet de concaténer tout type de variables pour obtenir une chaîne (un objet string). 5- Passages de paramètres en ligne de commande Il existe un autre prototype pour la fonction main : #include <iostream> int main(int argc,char **argv) std::cout << "arguments " << argc << "\n"; for(int i=0;i<argc;i++) std::cout << "argument " << i << " : " << argv[i] << "\n"; En enregistrant ce code dans un fichier test.cpp et en compilant pour créer l'exécutable test.exe, on peut ensuite faire ce type de passage de ligne de commande : test chaine1 12 chaine2 On obtient alors comme résultat affiché : C:\CPP>re chaine1 12 chaine2 arguments 4 argument 0 : re argument 1 : chaine1 argument 2 : 12
argument 3 : chaine2 Ie, on peut récupérer des paramètres passés en ligne de commande. On pourra ainsi utiliser l'invite de commande pour passer des paramètres pour certains programmes. Par exemple, on pourra passer un chemin pour les fichiers produit par le programme, ou bien une URL etc... à voir en fonction du programme. 6- Un exemple de récupération CSV Pour finir ce code, on propose un code de lecture d'un fichier CSV. On donne dans un premier temps la fonction qui permet d'obtenir un vecteur de vecteur de string depuis le fichier CSV. vector< vector<string> > recuperationcsv(string chemin) vector< vector<string> > liste; /*Ouverture du fichier*/ ifstream in(chemin.c_str()); string line; int comptelignes=0; /*Pour toute ligne du fichier*/ while(getline(in, line)) if(line[0]!='#') //std::cout << line << "\n"; vector<string> donneeslignecourante; int indice=0; while(indice<line.size()) string str(""); while(indice<line.size() && line[indice]!=' ' && line[indice]!='\t' && line[indice]!=',') str+=line[indice]; indice++; donneeslignecourante.push_back(str);
line[indice]=='\n')) while(indice<line.size() && (line[indice]==' ' line[indice]=='\t' line[indice]==',' indice++; liste.push_back(donneeslignecourante); return liste; Un exemple d'utilisation de ce code avec un fichier récupéré depuis yahoo!finance. Après avoir téléchargé le fichier AAF.txt sur le disque dur (qui correspond à l'une des actions sur yahoo), on utilise la fonction précédente pour récupérer une collection de collection de chaînes. Puis, dans le main, on met la série des cours dans une collection de double en inversant l'ordre des données (dans le fichier AAF.txt, les premiers cours donnés sont les plus récents, tandis que dans la collection cours, les premiers cours sont les plus anciens). #include <string> #include <iostream> #include <fstream> #include <sstream> #include <vector> using namespace std; vector< vector<string> > recuperationcsv(string chaine); int main() std::cout << "coucou\n"; /*On recupere l'ensemble du fichier*/ vector<vector <string> > v=recuperationcsv(string("d:\\enseignement\\cpp\\partie2\\aaf.txt")); /*on inverse la série des cours*/ vector< double> cours; for(int i=v.size()-1;i>=0;i--) string str=v.at(i).at(v.at(i).size()-1); istringstream in(str); double d;
in >> d; cours.push_back(d); for(int i=0;i<cours.size();i++) std::cout << cours.at(i) << "\n"; system("pause"); vector< vector<string> > recuperationcsv(string chemin) vector< vector<string> > liste; /*Ouverture du fichier*/ ifstream in(chaine.c_str()); string line; int comptelignes=0; /*Pour toute ligne du fichier*/ while(getline(in, line)) if(line[0]!='#') //std::cout << line << "\n"; vector<string> donneeslignecourante; int indice=0; while(indice<line.size()) string str(""); while(indice<line.size() && line[indice]!=' ' && line[indice]!='\t' && line[indice]!=',') str+=line[indice]; indice++; donneeslignecourante.push_back(str); line[indice]=='\n')) while(indice<line.size() && (line[indice]==' ' line[indice]=='\t' line[indice]==',' indice++;
liste.push_back(donneeslignecourante); return liste;