Files
CoursCPP/TP8/Bieres/main.cpp
2024-11-26 20:40:23 +01:00

303 lines
7.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <iostream>
#include <list>
#include <vector>
#include <fstream>
#include <string>
#include <algorithm>
#include <iomanip>
using namespace std;
class Biere {
string nom;
string type;
float deg_alcool;
float prix;
string region;
public:
void saisie(ifstream& stream);
// Saisie (mais depuis le clavier)
void saisie();
void affichage();
float get_deg_alcool() {
return deg_alcool;
}
float get_prix() {
return prix;
}
string get_nom() {
return nom;
}
};
// Ici la fonction lecture n'est pas une template
// car il doit ajouter au bon conteneur en fonction du type de bière
// On aurait pu aussi imaginer une template, avec un "filtre", qui n'ajoute que
// dans le conteneur passé en paramètre si le type == filtre.
// Mais cela aurait nécessité 2lectures du fichier (ou une autre fonction)
void lecture_fichier(list<Biere>& belges, vector<Biere>& etrangeres, const string fname) {
ifstream file(fname);
if (!file.is_open()) {
cout << "Erreur lors de l'ouverture du fichier" << endl;
return;
}
while (!file.eof()) {
string type;
Biere b;
getline(file,type, ';');
b.saisie(file);
if (type == "Belge") {
belges.push_back(b);
}
else {
etrangeres.push_back(b);
}
}
}
// La version template
// En soit le 2e paramètre (T) n'est pas vraiment nécessaire parce que toujours = "Biere"
// j'ai aucune idée de si il faut le mettre ou pas c'est un peu coup de poker avec
// Benjelloun, il peut te dire fallait pas le mettre pour la raison que j'ai cité ci-dessus
// mais aussi te dire mais il fallait le mettre (si on envisage l'inhéritance d'une classe bière)
template <class C, class T>
void lecture_fichier_filtre(C& conteneur, const string type, const string fname) {
ifstream file(fname);
if (!file.is_open()) {
cout << "Erreur lors de l'ouverture du fichier" << endl;
return;
}
while (!file.eof()) {
string t;
getline(file, t, ';');
if (t != type) {
file.ignore(200, '\n'); // passer la ligne, count=200 s'arrête dès que '\n'.
continue;
}
T b;
b.saisie(file);
conteneur.push_back(b);
}
}
template <class C>
void affichage(C& c) {
for (auto& a : c) {
a.affichage();
}
}
string saisie_type_biere() {
string t;
cout << "Belge ou pas (O/N) :";
cin >> t; cin.ignore();
return t == "O" ? "Belge" : "Etranger";
}
// Pareil ici pour le type T = "Biere"
// j'aime beacoup la syntaxe
// using T = typename C::value_type
// qui évite de devoir passer T explicitement
// mais pas sur que les assistants connaissent tous/ benjelloun apprécie
template <class C, class T>
void ajout(C& conteneur) {
T b;
b.saisie();
conteneur.insert(conteneur.begin(), b);
}
void saisie_seuil(int& s) {
cout << "Seuil : ";
cin >> s;
}
template <class C>
void erase_seuil_alcool(C& cont, float seuil) {
// au lieu d'itérer on aurait aussi pu utiliser remove_if (<algorithm>)
// mais à lors il aurait fallut passer le "seuil" en paramètre avec l'équivalent
// d'un "higher order function" ou d'un lambda
// mais pas vu au cours donc on fait comme ça.
auto it = cont.begin();
for (; it != cont.end(); /* il ne faut pas incrémenter l'itérateur à chaque fois, si on supprime on skip l'élement */) {
// it-> est équivalent à une déréférence (*it)
if (it->get_deg_alcool() >= seuil) {
it = cont.erase(it);
}
else {
it++;
}
}
}
// T= tjr bière en soit
template <class T>
bool comparer_prix(T& a, T& b) {
return a.get_prix() < b.get_prix();
}
template <class C>
void plus_cher(C& cont) {
// pour obtenir les bières LES + chers on peut soit
// itérer pour récupérer le max() en prix
// puis réitéré et n'afficher que les bières ou
// prix == max
// ou sort le conteneur O(N logN) puis prendre les N premier
// tant que N == prixPrecedent.
// max_element marchera pas pour PLUSIEURS bières :(
auto it = max_element(cont.begin(), cont.end(), comparer_prix<Biere>);
if (it == cont.end()) {
cout << "Aucune bière dans le conteneur";
return;
}
// équivalent à (*it).get_prix();
float prixMax = it->get_prix();
for (auto& b : cont) {
if (b.get_prix() == prixMax) {
// b.affichage();
cout << b.get_nom() << " au prix de " << setprecision(4) << b.get_prix() << " euros" << endl;
}
}
}
int menu() {
int choix;
cout << "1. Stocker les bières Belge(s) dans une List et les bières Etrangere(s) dans un Vector + Afficher" << endl
<< "2. Ajouter au début une bière à List ou au Vector des bières selon si la bière est Belge ou pas" << endl
<< "3. Supprimer les bières dont le degré d'alcool est supérieure à un seuil donné par lutilisateur" << endl
<< "4. Afficher les bières les plus chères du List et du Vector" << endl;
cout << "Choix:" << endl;
cin >> choix;
return choix;
}
int main() {
list<Biere> belges;
vector<Biere> etrangeres;
int seuil;
setlocale(LC_ALL, "");
while (true) {
switch (menu()) {
case 1:
// Ici, aucune idée de la bonne version
// Aussi un coup de poker avec Benjelloun.
// (lecture du fichier 2x vs pas de template+2conteneurs en paramètres)
// Avec la première fonction lecture
// lecture_fichier(belges, etrangeres, "Biere2.txt");
// Avec la seconde utilisant des templates et un filtre
lecture_fichier_filtre<list<Biere>, Biere>(belges, "Belge", "Biere2.txt");
lecture_fichier_filtre<vector<Biere>, Biere>(etrangeres, "Etrangere", "Biere2.txt");
cout << "Bières Belges:" << endl;
affichage(belges);
cout << "Bières étrangères" << endl;
affichage(etrangeres);
break;
case 2:
if (saisie_type_biere() == "Belge") {
ajout<list<Biere>, Biere>(belges);
cout << "Bières Belges:" << endl;
affichage(belges);
}
else {
ajout<vector<Biere>, Biere>(etrangeres);
cout << "Bières étrangères" << endl;
affichage(etrangeres);
}
break;
case 3:
saisie_seuil(seuil);
erase_seuil_alcool(belges, seuil);
erase_seuil_alcool(etrangeres, seuil);
cout << "Bières Belges:" << endl;
affichage(belges);
cout << "Bières étrangères" << endl;
affichage(etrangeres);
break;
case 4:
cout << "Les bières belges la plus chères sont" << endl;
plus_cher(belges);
cout << "Les bières étrangères la plus chères sont" << endl;
plus_cher(etrangeres);
break;
default:
return 0; // sortie
}
}
}
void Biere::saisie(ifstream& s) {
string tmp;
getline(s, nom, ';');
getline(s, type, ';');
getline(s, tmp, ';');
// POUR UNE RAISON QUE J'IGNORE complétement, sur VisualStudio 2022
// Avec windows en FRANCAIS, le stof lis les nombre avec des VIRGULES et pas des points
// Il est nécessaire de retirer ceci en fonction de la plateforme ou du compilateur
// Voir même de la langue de Windows (on pourra le faire mais je le fais pas)
replace(tmp.begin(), tmp.end(), '.', ',');
deg_alcool = stof(tmp); // fonction stof: String TO Float
getline(s, tmp, ';');
// Pareil ici :/
replace(tmp.begin(), tmp.end(), '.', ',');
prix = stof(tmp);
getline(s, region, '\n');
}
void Biere::affichage() {
cout << nom << "; " << type << "; " << deg_alcool << "; " << prix<<"; "<<region<<endl;
}
void Biere::saisie() {
cout << "Nom de la bière : ";
getline(cin, nom);
cout << "Un type : ";
getline(cin, type);
cout << "Un degré d'alcool : ";
cin >> deg_alcool;
cout << "Prix : ";
cin >> prix; cin.ignore();
cout << "Région/Pays : ";
getline(cin, region);
}