j'essaie de créer un parser d'image me permettant dans un premier tant d'ouvrir un fichier mini PNG et d'afficher certaines information sur l'image, puis ensuite afficher l'image en noir et blanc.
voici l’énoncé du problème :
Écrivez un parser lisant un fichier au format Mini-PNG et affichant les informations le concernant : largeur et hauteur de l’image, type de pixels. Voici le transcript attendu résultant de l’appel de votre programme sur le fichier A.mp : % ./Q1 A.mp Largeur : 8 Hauteur : 10 Type de pixel : 0 (noir et blanc)
Car pour récupérer ces données d'un PNG, ça se fait :
Selon la doc, après la signature du fichier, on a le segment IHDR (que j'ai souligné en vert),
En rouge, j'ai souligné le fait que c'est forcément le premier segment : ce qui fait que la position du IHDR est toujours la même.
Ensuite, comme tu le vois en bas à gauche, le segment IHDR contient tout de suite la largeur et hauteur, puis profondeur de pixel (ce que tu cherches).
Dans mon exemple, j'ai créé un PNG de 153x139 (ce qui fait en hexa 99 et 8B, qu'on retrouve souligné dans le fichier à partir de l'offset 0x10. Par contre, fait gaffe c'est du big-endian, et nos PC sont little-endian)
Le message qui suit est une réponse automatique activée par un membre de l'équipe de modération. Les réponses automatiques leur permettent d'éviter d'avoir à répéter de nombreuses fois la même chose, ce qui leur fait gagner du temps et leur permet de s'occuper des sujets qui méritent plus d'attention. Nous sommes néanmoins ouverts et si vous avez une question ou une remarque, n'hésitez pas à contacter la personne en question par Message Privé. Pour plus d'informations, nous vous invitons à lire les règles générales du forum
Merci de colorer votre code à l'aide du bouton Code
Les forums d'Openclassrooms disposent d'une fonctionnalité permettant de colorer et mettre en forme les codes source afin de les rendre plus lisibles et faciles à manipuler par les intervenants. Pour cela, il faut utiliser le bouton de l'éditeur, choisir un des langages proposés et coller votre code dans la zone prévue. Si vous utilisez l'éditeur de messages en mode Markdown, il faut utiliser les balises <pre class="brush: cpp;">Votre code ici</pre>.
Merci de modifier votre message d'origine en fonction.
#include <stdlib.h>
#include <stdio.h>
typedef struct Header{ // bloc entete du fichier image contenant les informations sur les caracteristiques de l'image
int largeur;
int hauteur;
int typePx;
}Header;
typedef struct Commentaire{ // il contient le descrptif de l'image
char C;
}Commentaire;
typedef struct bitmap { // informations permettant d'obtenir le bitmap de l'image
int PxCol;
int PxLn;
}bitmap;
int Convert (unsigned char hex[4],int nbre){ //Fonction pour convertir de l'hexadecimal au decimal
int result = 0;
int i;
for (i = nbre-1; i>=0; i--)
{
result = result * 256 + hex[i];
}
return result;
}
void lire_fichier (char nomFichier [255]){
unsigned char buffer[4]; // pour stocker temporairement les informations lus
FILE * fichier = NULL;
fichier = fopen (nomFichier,"rb");
fread (&Header.largeur,4,1,fichier); // l'erreur est signalée ici
fread (&buffer,4,1,fichier);
Header.largeur = Convert(buffer,4);
fread(&buffer,4,1,fichier);
fread(&header.hauteur,4,4,fichier);
fread(&buffer,4,5,fichier);
Header.hauteur = Convert(buffer,4);
fread(&buffer,4,5,fichier);
fclose(fichier);
}
l'intitulé de l'erreur est : [Error] expected primary-expression before '.' token
Bonjour. Plus d'infos ici sur le format d'image MiniPNG semble-t-il ...
On dirait même l'énoncé de l'exercice que le PO doit faire. Donc on oublie la structure PNG évoquée plus haut, on applique le principe selon les données de l'exercice. (ça ressemble quand même un peu au PNG)
umfred et magma oui c'est exactement le même projet que je dois faire alors si vous l'avez déjà réaliser merci de m'aider
je vais essayer de corriger pour l'utilisation du buffer.
mais c'est une question : Si je demande a un utilisateur de mon programme de rentrer un fichier image dois je utiliser une fonction speciale pour le permettre a mon prog de charger le fichier ou un scanf/fgets sera suffisant pour recuperer le fichier
Je ne vois pas ce "bug" dans l'énoncé de l'exercice (qui utilise du mini-PNG et non du PNG). Pour moi, il y a pour chaque bloc 5 octets à lire dans un 1er temps:
le 1er octet indiquant le type de bloc et donc le type d'info contenu dans les données du bloc
les 4 octets suivants qui détermine la longueur l des données à lire ensuite
Et donc dans un second temps, lire l octets de données à interpréter selon le type du bloc, et rien ne dit que les blocs sont dans l'ordre Header > Commentaire > Données
Puis ensuite continuer de lire les blocs jusqu'à la fin du fichier.
Pour le nom du fichier, il est dit qu'il sera passer par la ligne de commande, donc a priori, le nom du fichier est récupérable dans les paramètres du main. Le nom du fichier est une chaine de caractères, à ne pas confondre avec le fichier lui-même.
mais c'est une question : Si je demande a un utilisateur de mon programme de rentrer un fichier image dois je utiliser une fonction speciale pour le permettre a mon prog de charger le fichier ou un scanf/fgets sera suffisant pour recuperer le fichier
Ces deux fonctions sont orientées char *. Si tu veux lire un fichier dont tu ne connais pas à priori le contenu, fread() est nécessaire. Spécialement dans le cas de fichiers image, vidéo, bref de données dans le sens le plus large du mot, ceux qui contiennent des caractères non-human readable.
- Edité par edgarjacobs 17 octobre 2022 à 20:45:22
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
Reprends la description du fichier: le fichier commence par la chaine "Mini-PNG" donc 8 caractères (octets) pour le type du fichier; je ne vois pas cette lecture.
Ensuite tu as 5 octets pour définir le bloc (1 octet pour le type de bloc et 4 pour la longueur L du contenu du bloc) et ensuite tu as le contenu du blocs (L octets) et ce contenu est interprété selon le type de bloc. (rien ne dit que le bloc Header vient tout de suite après "Mini-PNG")
Donc tu suis un parcours en "Fondements de l'informatique et ingénierie du logiciel".
La première chose à faire avant même de pisser du code (en C ou quelqu'autre langage) est de bien lire le sujet et une fois qu'on a compris ce qu'on attend de toi de commencer par faire ressortir ce dont tu vas avoir besoin.
En très gros et après une lecture en diagonal, il apparaît que tu vas devoir pouvoir lire et écrire un fichier qui a un certain format. C'est un format par bloc, il en existe 3 différents (H, C et D).
Cela peut être une bonne idée de se dire que tu vas avoir besoin d'une structure représentant un bloc, que celui-ci aura un «type» particulier. Ici je mets type entre guillemets car il ne s'agit pas d'un type au sens «type de données C» mais plus d'un genre.
Ensuite il va te falloir écrire/lire un fichier. Une petite API dédiée serait également une bonne idée :
struct MPFILE {
mpfile_status status;
int fd;
…
};
qui va contenir un peu tout ce dont tu auras besoin pour les IO. Le champ status va décrire l'état = en gros tout est OK ou erreur d'accès, fichier invalide (=non mini png), fin de fichier atteinte, erreur en lecture, …
Mais bon, ça c'est un peu de la tambouille interne. Parce que ton programme va vraiment devoir manipuler n'est pas un fichier mais une image. Il te faut donc quelque chose qui va représenter une image :
umfred, effectivement j'ai corrigé le compte en tenant compte de tes remarques mais j'ai un décalage dans la lecture des bits comme si je reprenais le dernier bit de la precedente donnée.
voici le code et ce que ca donne a l'execution
#include <stdio.h>
#include <stdlib.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int hex2dec (unsigned char hex[4],int nombre) //Fonction permettant de convertir un nombre hexa
{ //en décimal (plus facile à lire)
int resultat = 0;
int i;
for (i = nombre - 1; i >= 0; i--)
{
resultat = resultat * 256 + hex[i];
}
return resultat;
}
struct header{
char d[8];
char tb;
int Lb;
int lb;
int hb;
int tp;
char ascii[255];
}h;
void lire_fichier (char nom[255])
{
unsigned char temp[8];
//Zone mémoire temporaire où on stoque ce qui est lu dans le fichier avant
FILE * fichier; //de le traiter.
fichier = fopen (nom,"rb");
fread (&temp,8,1,fichier);
h.d[0]=temp[0];
h.d[1]=temp[1];
h.d[2]=temp[2];
h.d[3]=temp[3];
h.d[4]=temp[4];
h.d[5]=temp[5];
h.d[6]=temp[6];
h.d[7]=temp[7];
fread (&temp,1,1,fichier);
h.tb=temp[0];
fread (&temp,4,1,fichier);
h.Lb=hex2dec(temp,4);
fread (&temp,4,1,fichier);
h.lb=hex2dec(temp,4);
fread (&temp,4,1,fichier);
h.hb=hex2dec(temp,4);
//Si c'est un image 8 bit, cette entête est suivie de la palette.
//Après la palette, ce sont les données de l'image
fclose (fichier);
}
int main(int argc, char *argv[]) {
lire_fichier ("A.mp");
printf ("type de bloc1 : %s\n",h.d);
printf ("type de bloc : %c\n",h.tb);
printf ("Longueur du bloc : %d\n",h.Lb);
printf ("largeur du bloc : %d\n",h.lb);
printf ("hauteur du bloc : %d\n",h.hb);
return 0;
}
ta fonction hex2dec() lit les nombres à l'envers (le dernier octet lu est *256*256*256 alors que ça devrait être le premier). Les valeurs lues valent respectivement: 9, 8 et 10
Quand on fait un projet comme ça, il ne faut pas s'imaginer qu'on va écrire directement le code du truc qu'on va rendre. Avant ça, il faut explorer un peu, d'une part parce qu'on ne comprend pas complètement le problème, d'autre part parce qu'on ne connait pas bien les moyens pour le programmer.
Ca fait toujours marrer quand les débutants arrivent avec "je galère, j'y arrive pas, je suis débutant". Ben tiens, évidemment. Déjà, quand on n'est pas débutant, la solution ne tombe pas du ciel non plus. Faut essayer des trucs avant de se lancer dans le programme demandé.
Bon, idée : on écrit un programme qui fait afficher les informations d'un fichier qui est au format demandé. Pour A.mp, ça donnerait
Magic number: Mini-PNG
bloc type H taille 9
- Image 8 x 10, pixels 0 - noir et blanc
bloc type C taille 11
- Commentaire: La lettre A
bloc type D taille 10
Le main, cherchons pas la complication
int main()
{
traiter_fichier("A.mp");
return EXIT_SUCCESS;
}
Dans une version améliorée, on ferait une boucle sur argc/argv pour traiter plusieurs fichiers. Mais peu importe, concentrons-nous sur le traitement du fichier.
Pour traiter le fichier, on l'ouvre, on lit le "magic number" et les blocs
J'ai préféré les fichiers bas niveau, si vous voulez des FILE*/fopen/fread/... vous faites comme vous voulez profitez-en on est en république tant que c'est pas moi le chef.
Pour le magic number, on doit lire 8 octets dans le fichier, on utilise un tampon de 9 pour en faire une chaine à la C
Pour traiter les blocs, il faut faire une boucle, parce qu'il y en a plusieurs. Dans la boucle, on essaie de lire l'entete de bloc et si ça marche, on lit et on traite les données du bloc.
struct entete_bloc {
char type;
int taille_contenu;
};
void traiter_blocs(int fd)
{
while (true) {
struct entete_bloc h;
if (! traiter_entete_bloc(fd, &h)) break;
printf("bloc type %c taille %d\n", h.type, h.taille_contenu);
traiter_contenu_bloc(fd, &h);
}
}
L'entete du bloc c'est sur 5 octets, dont 1 pour le type et 4 pour un entier qu'il faut décoder
int valeur_entier(const char octets[], int taille)
{
int valeur = 0;
for (int i=0; i < taille; i++) {
valeur = (valeur << 8) | octets[i];
}
return valeur;
}
bool traiter_entete_bloc(int fd, struct entete_bloc *h)
{
char data[1 + 4];
int n = read(fd, data, 5);
if (n <= 0) return false; // y en a plus
h->type = data[0];
h->taille_contenu = valeur_entier(data + 1, 4);
return true; // y en a
}
Rappel : tableau + indice, ça donne l'adresse de tableau[indice].
Maintenant la lecture et le traitement du bloc : on alloue dynamiquement un tampon de la taille voulue, on y met les données prises dans le fichier, on les traite, et on n'oublie pas de rendre le tampon (ici on ne s'en sert plus, donc on évite la fuite mémoire)
void traiter_contenu_bloc(int fd, const struct entete_bloc *h)
{
char *data = malloc(h->taille_contenu);
read(fd, data, h->taille_contenu);
// traitement à détailler selon le type
switch (h->type) {
case 'H' :
afficher_caracteristiques(data);
break;
case 'C' :
afficher_commentaire(data, h->taille_contenu);
break;
default:
break;
}
free(data);
}
Les données d'un bloc de commentaires, c'est du texte, mais pas contenu dans une chaine avec un terminateur. Une solution, c'est de l'afficher par fwrite
Quelques includes au début du fichier, et ça roule.
A tester sur plusieurs fichiers
Etape d'aprés :
sauvegarder précieusement ce qui a été fait dans l'étape exploratoire.
En faire une copie et la bidouiller pour que ça réponde aux questions, une par une.
IMPORTANT : sécuriser. Par exemple,
pour chaque read, vérifier que le nombre de caractères lus correspond à ce qui était demandé.
vérifier le magic number
vérifier les champs qui ont des valeurs contraintes : types des blocs / types de pixel ...
Ne nous cachons pas la vérité : le choix du langage C va conduire au maximum d'emmerdements, en l'absence de mécanisme de gestion des erreurs (exceptions par exemple).
- Edité par michelbillaud 21 octobre 2022 à 17:18:43
J'avais fait des essais concluants de code hier sur un compilateur en ligne (https://www.onlinegdb.com/online_c_compiler) mais aujourd'hui j'avais des valeurs comme les tiennes, le fichier A.mp enregistré sur le site avait été altéré (tous les octets nuls ont été supprimés, ce qui me faussait la lecture )
je crois que j'avance tout doucement faire l'objectif de ce projet, mais j'ai toujours besoin du regard éclairé des experts.
je vous montre ce que j'ai finalement réussi a faire en utilisant des fonctions plus adaptées a mes besoins mais il y a quand même des aspects qui me chiffonne une peu
#include <stdlib.h>
#include <stdio.h>
#define S_SIZE 9 // taille du tableau contenant la signature
struct Header{ // bloc entete du fichier image contenant les informations sur les caracteristiques de l'image
// char signature[S_SIZE]; // Pour la signature de l'image
// char typeBloc;
int largeur;
int hauteur;
char typePx;
}H;
struct Commentaire{ // il contient le descrptif de l'image
char C[12];
}Comment;
struct bitmap { // informations permettant d'obtenir le bitmap de l'image
int PxCol;
int PxLn;
}bmp;
void lire_fichier (char nomFichier [255]){
FILE * fichier = NULL;
if ( (fichier = fopen(nomFichier, "rb")) == NULL ) {
printf("Je ne peux pas ouvrir %s", nomFichier);
exit(1);
}
else {
// fseek (fichier,0,SEEK_SET); // lire la signature du fichier
// fgets (H.signature,S_SIZE,fichier);
//
//
// fseek(fichier,8,SEEK_SET); // lire de type de bloc
// H.typeBloc = fgetc(fichier);
fseek(fichier,16,SEEK_SET); // lire la largeur de l'image
H.largeur = fgetc(fichier);
fseek(fichier,20,SEEK_SET); // lire la hauteur de l'image
H.hauteur = fgetc(fichier);
fseek(fichier,21,SEEK_SET); // lire le type de pixel
H.typePx = fgetc(fichier);
fseek(fichier,27,SEEK_SET);
fgets(Comment.C,12,fichier); // lire le commentaire de l'image
fclose(fichier);
}
}
int main(int argc, char *argv[]){
lire_fichier("A.mp");
// printf("la signature du fichier est : %s\n",H.signature);
// printf("le type de bloc est : %c\n",H.typeBloc);
printf("la largeur de l'image est : %d\n",H.largeur);
printf("Le nombre d'octets de la largeur de l'image est : %d\n\n",sizeof(H.largeur));
printf("la hauteur de l'image est : %d\n",H.hauteur);
printf("Le nombre d'octets de la hauteur de l'image est : %d\n\n",sizeof(H.hauteur));
printf("le type de pixel est : %d (noir et blanc)\n",H.typePx);
printf("Le nombre d'octets du type de pixels est : %d\n\n",sizeof(H.typePx));
printf("Commentaire : %s\n",Comment.C);
printf("Le nombre d'octets du commentaire est : %d\n\n",sizeof(Comment.C));
return 0;
}
j'ai fait les 3 premières questions du projet mais mes préoccupations sont les suivantes :
- au niveau du commentaire j'ai fait quelque chose qui ne me satisfait pas je veux quelque de plus universel
- pour le test a faire pour verifier les tailles j'ai juste utilisé sizeof mais je me demande si c'est vraiment ce que je devais faire
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html
l'intitulé de l'erreur est : [Error] expected primary-expression before '.' token
Recueil de code C et C++ http://fvirtman.free.fr/recueil/index.html
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent
En recherche d'emploi.