303 lines
7.2 KiB
C++
303 lines
7.2 KiB
C++
#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 l’utilisateur" << 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);
|
||
} |