TP8: bieres

This commit is contained in:
2024-11-26 20:28:03 +01:00
commit 29bbe37f35
8 changed files with 516 additions and 0 deletions

303
TP8/Bieres/main.cpp Normal file
View File

@@ -0,0 +1,303 @@
#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 ont peu 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);
}