TD 8 : fichiers et images numériques#
Notes aux enseignants
Dans ce TD et dans le TP, nous ferons la déclaration et l’ouverture du fichier en deux temps, comme dans le cours (dernière version). Les élèves ayant en général du mal avec la gestion des fichiers, avant d’attaquer l’exercice 1 il peut être utile d’écrire au tableau
ifstream flux;
flux.open("bonjour.txt");
et de demander aux élèves ce que fait cette instruction, et de même
avec ofstream
.
En profiter pour rappeler qu’ils doivent relire systématiquement leur cours avant le TD.
Concernant les images, elles n’ont pas été vues en cours. S’arranger pour avoir le temps d’évoquer au moins rapidement la correction des exos sur les images, car l’avoir compris est nécessaire pour le TP.
Exercice 1 : Fichiers
On considère la fonction mystere
suivante, avec un exemple d’appel :
void mystere(string nomFichier) {
ifstream flux;
flux.open(nomFichier);
string t;
int n;
while ( flux >> t and flux >> n ) {
cout << t << " " << 2 * n << endl;
}
flux.close();
}
mystere("truc.txt");
Le fichier truc.txt
contient les premières lignes suivantes :
Henry 4
Messi 2
Ronaldo 3
Xavi 2
Expliquez ce que fait la fonction
mystere
. Précisez le format que doit avoir le fichier. Choisissez des noms plus informatifs pour cette fonction et ses variables, et écrivez sa documentation./// BEGIN SOLUTION
La fonction lit le fichier dont le nom est pris en paramètre. Ce fichier doit contenir une suite de chaînes de caractères (par exemple des noms) suivie chacune d’un entier (par exemple une note). Elle affiche chaque chaîne suivie de l’entier multiplié par 2.
/** affiche les notes contenues dans un fichier après les avoir * multipliées par deux. * Format: chaque ligne doit être de la forme "<nom> <note>" * @param nomFichier le nom du fichier **/ void afficheNotes(string nomFichier) { ifstream flux; flux.open(nomFichier); string nom; int noteSur10; while ( flux >> nom and flux >> noteSur10 ) { cout << nom << " " << 2 * noteSur10 << endl; } flux.close(); }
/// END SOLUTION
Changez la fonction pour que chaque ligne de la sortie soit de la forme :
Nom : Alfred, note sur 10 : 7, note sur 20 : 14
/// BEGIN SOLUTION
Il suffit de remplacer l’instruction de la boucle while par :
cout << "Nom : " << nom << ", note sur 10 : " << noteSur10 << ", note sur 20 : " << 2 * noteSur10 << endl;
/// END SOLUTION
Notes aux enseignants
Une des motivations pour faire renvoyer la moyenne comme un entier est que cela simplifie le test automatique avec CHECK.
Exercice 2 : Fichiers
Définissez les deux fonctions suivantes :
/** calcule la moyenne des notes contenues dans un fichier texte * Format: chaque ligne du fichier est de la forme "<nom> <note>" * @param nomFichier le nom du fichier * @return la moyenne des notes, arrondie à sa partie entière **/ int moyenne(string nomFichier);
/** lit les notes contenues dans un fichier et en fait un tableau * Format: chaque ligne du fichier est de la forme "<nom> <note>" * @param nomFichier le nom du fichier * @return un tableau contenant les notes **/ vector<int> lit_notes(string nomFichier);
/// BEGIN SOLUTION
int moyenne(string nomFichier) { ifstream flux; flux.open(nomFichier); string temp; int note; int somme = 0; int nb = 0; while ( flux >> temp and flux >> note ) { somme += note; nb += 1; } flux.close(); return somme / nb; } vector<int> lit_notes(string nomFichier) { ifstream flux; flux.open(nomFichier); vector<int> notes; string temp; int note; while ( flux >> temp and flux >> note ) { notes.push_back(note); } flux.close(); return notes; }
/// END SOLUTION
Lorsque cela est possible, écrivez un test.
/// BEGIN SOLUTION
Après avoir écrit un fichier de test, on peut faire des tests automatiques :
CHECK( moyenne("truc.txt") == 3 ); CHECK( lit_notes("truc.txt") == vector<int>( {4,2,3,2,1,5,5,4,4,5} ) );
/// END SOLUTION
\(\clubsuit\) Comment pourrait-on tester la fonction du premier exercice?
/// BEGIN SOLUTION
Cette fonction est plus difficile à tester automatiquement car elle affiche son résultat plutôt que de le renvoyer.
Il faudrait donc séparer calcul et affichage, à l’aide d’une fonction qui renvoie le résultat comme une chaîne de caractères – que l’on pourra donc facilement tester avec
CHECK
./// END SOLUTION
Exercice 3 : Images numériques
Pour manipuler informatiquement une image analogique (continue), on doit la numériser, c’est à dire l’encoder sous forme d’une image numérique (discrète). Il en existe deux grandes catégories :
Les images vectorielles : une image y est encodée par une combinaison de primitives géométriques (lignes, disques, …) auxquelles sont appliquées des transformations. En savoir plus : https://fr.wikipedia.org/wiki/Image_vectorielle
Les images matricielles, ou « carte de points » (de l’anglais bitmap): une image y est constituée d’une matrice – ou tableau ou grille – où chaque case – appellée pixel – possède une couleur qui lui est propre. Il s’agit donc d’une juxtaposition de carrés de couleur formant, dans leur ensemble, une image. En savoir plus : https://fr.wikipedia.org/wiki/Image_matricielle
Images en noir et blanc. Le fichier suivant contient une image en noir et blanc au format PBM (Portable Bit Map). Devinez comment fonctionne ce format de fichier et dessinez l’image correspondante.
P1 # CREATOR: GIMP PNM Filter Version 1.1 10 10 0 0 0 1 1 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 1 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 1 0 0 1 0 1 0 0 1 1 0 0 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1 1 1 1 0 0 0
Indications : La première ligne précise le format du fichier (texte, image noir et blanc); la deuxième est un commentaire; tout le reste a un rôle! Voir https://fr.wikipedia.org/wiki/Portable_bitmap pour plus d’informations sur ce format de fichier.
/// BEGIN SOLUTION
La ligne
10 10
indique qu’il s’agit d’une image de largeur 10 pixels et de hauteur 10 pixels. La suite décrit les pixels de l’image (0 pour blanc, 1 pour noir), en la parcourant de haut en bas et de gauche à droite (comme la lecture d’un texte en français).Cela donne :
/// END SOLUTION
Images en couleur. Le fichier suivant contient une image en couleur au format PPM (Portable Pix Map). Devinez comment fonctionne ce format de fichier et dessinez l’image correspondante.
P3 # CREATOR: GIMP PNM Filter Version 1.1 3 2 255 0 255 0 255 255 255 255 0 0 0 255 0 255 255 255 255 0 0
Indication : Quelles sont les trois couleurs primaires usuelles (pour les écrans)?
/// BEGIN SOLUTION
La ligne
P3
précise le format du fichier (texte, image couleur RGB). La ligne3 2
indique qu’il s’agit d’une image avec 2 lignes de 3 pixels. La ligne255
donne la valeur de l’intensité maximale. Ensuite chaque pixel est codé par trois valeurs (RGB = rouge, vert, bleu) comprises entre 0 et l’intensité maximale.Cela donne :
/// END SOLUTION
Notes aux enseignants
Certains élèves confondent les couleurs primaires de la lumière (synthèse additive : écrans, …): rouge, vert et bleu, et celles de la matière (synthèse soustractive : peinture, …): magenta (rouge), cyan (bleu) et jaune.
Exercice 4 : \(\clubsuit\)
Définissez les fonctions suivantes :
/** compte le nombre de mots d'un fichier texte
* @param nomFichier le nom du fichier
* @return le nombre de mots contenus dans le fichier
**/
int word_count(string nomFichier);
/// BEGIN SOLUTION
int word_count(string nomFichier) {
ifstream flux;
flux.open(nomFichier);
int nbr = 0;
string temp;
while ( flux >> temp ) {
nbr += 1;
}
flux.close();
return nbr;
}
/// END SOLUTION
/** compte le nombre de lignes d'un fichier texte
* @param nomFichier le nom du fichier
* @return le nombre de lignes contenues dans le fichier
**/
int line_count(string nomFichier);
/// BEGIN SOLUTION
int line_count(string nomFichier) {
ifstream flux;
flux.open(nomFichier);
string ligne;
int compteur = 0;
while ( getline(flux, ligne) ) {
compteur += 1;
}
flux.close();
return compteur;
}
/// END SOLUTION
/** affiche (sur la sortie standard) le contenu d'un fichier texte
* @param nomFichier le nom du fichier
**/
void cat(string nomFichier);
/// BEGIN SOLUTION
void cat(string nomFichier) {
ifstream flux;
flux.open(nomFichier);
string ligne;
while ( getline(flux,ligne) ) {
cout << ligne << endl;
}
flux.close();
}
/// END SOLUTION
/** copie le contenu d'un fichier texte dans un autre
* @param source le nom du fichier source
* @param destination le nom du fichier destination
**/
void copy(string source, string destination);
Indication : la bibliothèque standard fournit la fonction suivante :
/** lit une ligne d'un flux et la stocke dans une chaîne de caractères
* @param flux un flux entrant
* @param s une chaîne de caractères
* @return le flux entrant
**/
istream getline(istream &flux, string &s);
/// BEGIN SOLUTION
void copy(string source, string destination) {
ifstream fluxSource;
fluxSource.open(source);
ofstream fluxDestination;
fluxDestination.open(destination);
string ligne;
while ( getline(fluxSource, ligne) ) {
fluxDestination << ligne << endl;
}
fluxSource.close();
fluxDestination.close();
}
/// END SOLUTION
Exercice 5 : \(\clubsuit\)
Devinez ce que fait la fonction mystereEpais
suivante et écrire un
test :
int mystereEpais(string zut) {
ifstream bla;
bla.open(zut);
int foo = 0;
char y;
while ( bla >> y ) {
foo++;
}
bla.close();
return foo;
}
/// BEGIN SOLUTION
La fonction renvoie le nombre de caractères du fichier en entrée (sans compter les espaces et retours à la ligne : ils sont considérés comme de simples délimiteurs).
Exemple de test :
CHECK( mystereEpais("truc.txt") == 65 );
/// END SOLUTION
Exercice 6 : \(\clubsuit\)
On dispose de trois fichiers texte nommés note1.txt
, note2.txt
, et
note3.txt
. Dans chacun d’eux, chaque ligne contient trois
informations, séparées par des espaces : le nom d’un-e étudiant-e, son
groupe, une note. Les trois fichiers pourraient par exemple
correspondre aux notes dans trois matières pour une même promotion.
Écrivez un programme qui:
Fusionne ces trois fichiers en un seul fichier
notes.txt
dont chaque ligne aura cinq informations séparées par des espaces :<nom> <groupe> <note1> <note2> <note3>
. On vérifiera au passage que les trois fichiers de départ contiennent exactement les mêmes noms d’étudiant-es et dans le même ordre.Crée un fichier par groupe, qui indiquera, pour chaque étudiant-e de ce groupe, sa note moyenne (moyenne des trois notes), au format
<nom> <note_moyenne>
. Les fichiers seront nommésgroupeB5.txt
,groupeA1.txt
,groupeC3.txt
. Ce programme doit pouvoir fonctionner quel que soit le nombre de groupes et quels que soient les noms de ces groupes.
/// BEGIN SOLUTION
#include <fstream>
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<string> fic_notes = {"note1.txt", "note2.txt", "note3.txt"};
int nb_notes = fic_notes.size();
int k;
string nom, gpe, nom_fusion;
double note, somme;
vector<double> notes(nb_notes);
// la taille de chaque fichier de notes
vector<int> taille_fichier(nb_notes);
for ( int i = 0; i < nb_notes; i++ ) {
ifstream f;
f.open(fic_notes[i]);
taille_fichier[i] = 0;
// On lit chaque ligne : nom, gpe, note.
while ( f >> nom >> gpe >> note ) {
taille_fichier[i]++;
}
f.close();
}
// Si les fichiers ne sont pas tous de même taille, on s'arrête.
bool meme_taille = true;
k = 1;
while ( k < nb_notes and meme_taille ) {
meme_taille = (taille_fichier[k] == taille_fichier[0]);
k++;
}
if ( not meme_taille ) {
cout << "les fichiers de notes ne sont pas de même taille" << endl;
} else {
// On recopie nom, groupe, note1 dans le fichier fusionné.
ifstream note1_lire;
note1_lire.open(fic_notes[0]);
ofstream fusion_ecrire;
fusion_ecrire.open("notes.txt");
while ( note1_lire >> nom >> gpe >> note ) {
fusion_ecrire << nom << ' ' << gpe << ' ' << note << endl;
}
note1_lire.close();
fusion_ecrire.close();
// On recopie les notes suivantes dans le fichier fusionné,
// avec recopie dans un fichier temporaire à chaque étape.
// On vérifie au passage que les noms sont dans le même ordre.
// NB : on aurait pu vérifier aussi les noms des groupes.
bool erreur_nom = false;
k = 1;
while ( not erreur_nom and k < nb_notes ) {
ifstream fusion_lire;
fusion_lire.open("notes.txt");
ofstream temp_ecrire;
temp_ecrire.open("temp.txt");
ifstream notek_lire;
notek_lire.open(fic_notes[k]);
while ( not erreur_nom
and notek_lire >> nom >> gpe
and fusion_lire >> nom_fusion >> gpe) {
erreur_nom = (nom != nom_fusion);
if ( erreur_nom ) {
cout << "pas les mêmes noms" << endl;
} else {
temp_ecrire << nom << ' ' << gpe;
for ( int i = 0; i < k; i++) {
if ( fusion_lire >> notes[i] ) {
temp_ecrire << ' ' << notes[i];
}
}
if ( notek_lire >> notes[k] ) {
temp_ecrire << ' ' << notes[k] << endl;
}
}
}
temp_ecrire.close();
fusion_lire.close();
notek_lire.close();
// On recopie le fichier temporaire dans le fichier fusionné
ifstream temp_lire;
temp_lire.open("temp.txt");
ofstream fusion_ecrire;
fusion_ecrire.open("notes.txt");
while ( temp_lire >> nom >> gpe ) {
fusion_ecrire << nom << ' ' << gpe;
for ( int i = 0; i <= k; i++ ) {
if ( temp_lire >> notes[i] ) {
fusion_ecrire << ' ' << notes[i];
}
}
fusion_ecrire << endl;
}
temp_lire.close();
fusion_ecrire.close();
k++;
}
if ( not erreur_nom ) {
// On détermine la liste des groupes.
// vector<string> groupes(taille_fichier[0]);
// Au maximum, il y aura un groupe par étudiant.
vector<string> groupes;
ifstream f;
f.open("notes.txt");
bool present;
int j;
while ( f >> nom >> gpe ) {
present = false;
j = 0;
while ( j < groupes.size() and not present ) {
present = (gpe == groupes[j]);
j++;
}
if ( not present ) {
groupes.push_back(gpe);
}
for ( int i = 0; i < nb_notes; i++ ) {
f >> notes[i];
}
}
f.close();
// On écrit un fichier pour chaque groupe.
for ( int p = 0; p < groupes.size(); p++ ) {
ofstream fgpe;
fgpe.open("groupe" + groupes[p] + ".txt");
ifstream fusion;
fusion.open("notes.txt");
while ( fusion >> nom >> gpe ) {
for ( int i = 0; i < nb_notes; i++ ) {
fusion >> notes[i];
}
if ( gpe == groupes[p] ) { // si c'est le même groupe
// on calcule la somme des nb_notes notes
somme = 0;
for ( int n = 0; n < nb_notes; n++ ) {
somme += notes[n];
}
fgpe << nom << ' ' << somme / nb_notes << endl;
}
}
fgpe.close();
fusion.close();
}
}
}
return 0;
}
/// END SOLUTION