Heu, pas si Ok.
La version "Février 6" ne compile pas chez moi (et chez les autres aussi, je pense).
La fonction "Serie:: PrintDate_ou_Dates()" indique retourner un "const bool" mais votre implémentation ne retourne rien.
Comme un "Printxxx" n'a pas vraiment besoin de renvoyer quoi que ce soit (avec les paradigmes C++ avec exception ; en C on aurait du renvoyé un "booleén"), on va changer la signature de
const bool Serie::PrintDate_ou_Dates()
en
void Serie::PrintDate_ou_Dates()
et cela aussi bien dans Exemple.cpp que dans Exemple.h.
Et là, pouf, ça compile et à l'exécution, le programme retourne 0, tout est "OK".
Essayez de mettre des "labels/noms de version" sur une version du code source qui compile. SVP.
Pour la fonction "lire_fichierTxt(std::wstring const& nomFichier, std::vector<std::wstring> separeteurs)" :
ifstream fichier{ nomFichier };
if (!fichier)
{
throw std::runtime_error("Fichier impossible à ouvrir.");
}
Vous n'indiquez pas précisément votre question, alors je vais faire quelques remarques, mais c'est quand même un code "acceptable" pour un débutant.
Vous avez 2 fonctions :
const std::vector<std::wstring> lire_fichierTxt(std::wstring const& nomFichier, std::vector<std::wstring> separeteurs)
const std::wstring lire_fichierTxt(std::wstring const& nomFichier)
Ces 2 fonctions portent le même nom, c'est pas un problème en C++.
Mais, généralement, c'est qu'elles font des choses identiques mais à partir de données différentes.
Ici, ça semble être le cas, mais le type de données retournés est différent, il faudrait harmoniser la chose.
Soit c'est vraiment "la même chose" et dans ce cas, il faudra avoir le même type de retour, celui le plus adapté au code qui devra s'en servir. (cas 1)
Soit c'est des choses différentes, alors ces fonctions devraient avoir des noms différents, pour le ne pas "tromper" les utilisateurs de ces fonctions. (cas 2)
Il y a beaucoup de codes communs entre ces 2 fonctions, ce qui est normal si l'on est dans le cas 1.
Dans le cas 1, "lire_fichierTxt(std::wstring const& nomFichier)" serait un cas particulier de "lire_fichierTxt(std::wstring const& nomFichier, std::vector<std::wstring> separeteurs)" avec une liste de séparateur vide.
Il est alors facile de remplacer le corps de la première fonction par juste un appel à la seconde fonction.
1 2 3 4
| const std::vector<std::wstring> lire_fichierTxt(std::wstring const& nomFichier)
{
return lire_fichierTxt(nomFichier, std::vector<std::wstring>());
} |
Moins de code à maintenir, c'est moins de de bugs.
Le code actuel de votre fonction "lire_fichierTxt(std::wstring const& nomFichier, std::vector<std::wstring> separeteurs)" ne gère pas correctement ce cas d'usage, pensez à le corriger.
Il faut distinguer les erreurs qui sont dû à une erreurs de programmation à une erreur "runtime"(valeur entrée par l'utilisateur erronée, gestion d'un cas non spécifié (plus de mémoire, problème de droits, etc...)).
Dans ce "throw" en ligne 10, on ne sait pas trop ce qu'on cherche à signaler.
Si c'est un cas d'erreur de programmation, un "assert" au lieu d'un throw serait plus pertinent.
Si c'est un cas d'erreur "runtime", vous ne donnez pas assez d'information pour utilisable par l'utilisateur de l'application, comme le "nom"(le chemin) du fichier qui pose problème.
nom/chemin que vous spécifiez dans le message d'erreur en ligne 20.
Si vous savez pas trop, commencez par un "assert" tant que vous n'avez pas organisé votre code.
Normalement, le cas d'un fichier qui n'existe pas devrait avoir été signalé en amont, dés la saisie de celui-ci dans la console ou la lecture dans un fichier, pour avoir un contexte d'erreur précis.
Donc "assert(fichier)" ne devrait pas se déclencher sur un simple fichier inexistant. Si vous l'avez sur ce cas, c'est que les vérifications en amont sont défaillantes => erreur de programmation donc assert pertinent).
Pour renforcer la sécurité de la fonction, vous devriez changer le type du paramètre "nomFichier" en un type "std::path" pour indiquer que l'existence du fichier doit être vérifié avant l'appel (il y a peut-être des types encore plus pertinent que "std::path").
Si le problème est un cas de droit d'accès ou de fichier qui à disparu, c'est des cas que vous ne gérer pas encore, donc assert pertinent.
Si tous les cas que devraient gérer le programme sont là, vous pouvez transformer le "assert' en un "throw", mais avec les informations pertinentes pour l'utilisateur du programme.
Attention, une fonction de 63 lignes (il y a pas mal de lignes blanches ou en commentaire, je sais), ça commence à être un peu long. Pensez à factoriser le code avec des sous-fonctions réutilisables.
exemple de sous fonctions possible :
- le code des lignes 13 à 16 qui lit le contenu d'un fichier et le stocke dans un tableau de ligne.
- le code des lignes 24 à 26 qui convertit une chaine utf-8 en std::wstring (avec un rtrim pour une mise ne forme ?)
- etc...
Cela permet d'avoir moins de code à maintenir et du code plus court et facile à lire.
Vous faites souvent des copier-coller de code, pensez à faire des fonctions dédiées à la place de ces copier-coller.
Vous faites souvent des recherches de caractères/motifs dans ces chaines de caractères. Il existe un concept en programmation : "les expressions régulières", qui devrait faciliter l'écriture du code faisant ces recherches.
Erreur ou pas : Problème !!!
Quelle est la question ?
1 2 3 4 5 6 7 8 9 10 11
| Dans Exemple.h :
Serie(std::filesystem::path racine);
...
std::filesystem::path racine;
...
Dans exemple.cpp :
std::filesystem::path filePath(p);
Serie::Serie(std::filesystem::path racine)
{
...
} |
Idem, quelle est la question ?
afficher_Titres(nomFichier); ???
Merci
Idem, quelle est la question ?
1 2 3
| const std::wstring racine = t + L"Better Call Saul.[2015 - 2022]";
std::wcout << L"racine=[" << racine << L']' << std::endl;
Serie serie(std::filesystem::path racine); |
Heu, ça "compile" mais ça fait pas ce que vous pensez.
C'est mieux comme ça :
1 2 3
| const std::wstring racine = t + L"Better Call Saul.[2015 - 2022]";
std::wcout << L"racine=[" << racine << L']' << std::endl;
Serie serie(racine); |
std::wcout << L"serie.racine=[" << ??? << L"]" << std::endl;
(On n'est d'accord que l'utilisation de "std::wcout", c'est juste pour faire des tests dans votre projet "Exemple", sinon, je vais me fâcher
)
Il y a un choix à faire.
Soit le champ "racine" est un champ public, soit il ne l'ai pas.
Si c'est un champ public, tout utilisateur de la classe Serie pourra accéder directement à ce champ via un code comme :
std::wcout << L"serie.racine=[" << serie.racine << L"]" << std::endl;
C'est bien, mais ça fait du champ "racine" un élément de l'interface PUBLIC de la classe Serie.
Ça veut dire que vous ne pourrez pas facilement modifier le comportement de cette classe Serie, car tout code utilisateur de la classe Serie qui utilisera ce champ sera cassé si vous changez la nature de ce champ (public/privé, son type, son existence ou pas, son utilisation interne dans la classe).
Ce n'est pas un problème pour des classes "basiques" comme les structures en C, qui n'ont pas de "comportement" associés, mais pour des classes plus complexes cela devient problématique.
Comme je ne sais pas ce que vous allez en faire je ne peux pas dire s'il faut que "racine" soit un champ public ou protected/privé.
Si le champ "racine" est un champ non public, la méthode habituelle est de créer un "accesseur".
Un accesseur c'est une fonction qui à pour rôle de faire en sorte que le code utilisateur de la classe ait accès à des informations "plus ou moins interne" à l'objet.
Par exemple, en ajoutant la ligne suivante dans la section publique de la déclaration de la classe Serie :
1 2 3 4 5 6 7
| class Serie
{
public:
...
std::filesystem::path getRacine() { return racine; };
...
} |
On crée un accesseur "getRacine" qui sera utilisable ainsi :
std::wcout << L"serie.racine=[" << serie1.getRacine() << L"]" << std::endl;
En utilisant un accesseur, si vous voulez changer comment fonctionne en interne votre classe Serie, vous pourrez toujours modifier l'implémentation de l'accesseur pour que le code utilisant ce champ fonctionne toujours.
for (directory_iterator next(std::filesystem::path racine), end; next != end; ++next)
Je ne vois pas ce code dans le projet exemple, je ne vois que du code qui utilise correctement ce type de syntaxe :
for (directory_iterator next(racine), end; next != end; ++next)
Partager