IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Lecture fichier : char inférieur à 0


Sujet :

C++

  1. #1
    Membre averti
    Profil pro
    Pilote / Testeur de tombereaux - Caterpillar
    Inscrit en
    Août 2008
    Messages
    28
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Pilote / Testeur de tombereaux - Caterpillar
    Secteur : Bâtiment Travaux Publics

    Informations forums :
    Inscription : Août 2008
    Messages : 28
    Par défaut Lecture fichier : char inférieur à 0
    Bonjour,

    Ceci est mon premier post sur ce forum. J'ai une année de pratique du C++.

    ---

    Voici ma question :

    Ce petit bout de code me donne un resultat que je trouve étrange :
    Il s'agit d'ouvrir un fichier bmp et d'en lire les 5 premiers caractères

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    ifstream fichier("test.bmp", ios::in | ios::binary);	
     
    	for(short int i = 0; i<5; ++i)
    	{
    		char c;
    		fichier.get(c);
     
    		cout<<(int) c<<endl;
    	}
     
    fichier.close();
    Sortie :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    66
    77
    -8
    -41
    13
    On remarque que les deux premiers caractères (66 et 77) correspondent respectivement au 'B' et 'M' de l'en-tête du fichier BMP.

    Seulement, pourquoi est-ce que les deux caractères suivants sont négatifs ?

    De plus, si on exécute cet autre bout de code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    ifstream f("test.bmp", ios::in | ios::binary);	
     
    	int i = 0;
    	char c[2];
     
    	f.read( &c[0], sizeof(char) );
    	f.read( &c[1], sizeof(char) );
     
    	f.read( (char *) &i, sizeof(int) );
     
    	cout<< c <<endl;
    	cout<< i <<endl;
     
    f.close();
    on obtient :

    Où 907 256 correspond à la taille du fichier bmp en octet (cf. résumé sur le format bmp : http://andrew.csie.ncyu.edu.tw/zip/Bmp1.htm)

    Ne devrais-je pas avoir en sortie:
    ?

    Je vous remercie d'avance pour vos réponses

    Damien

    ---

    Note : voici le début de l'en-tête d'un fichier bmp

    +--------+--------+--------+--------+
    | Char 'B' | Char 'M'| Taille en [...] |
    +--------+--------+--------+--------+
    | octets du fichier |
    +--------+--------+--------+--------+

  2. #2
    Modérateur
    Avatar de nouknouk
    Homme Profil pro
    Inscrit en
    Décembre 2006
    Messages
    1 655
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France

    Informations forums :
    Inscription : Décembre 2006
    Messages : 1 655
    Par défaut
    salut et bienvenue !

    en C/C++ un char représente une valeur signée stockée sur 8 bits (un octet).

    Je pense que dans ton cas tu voudrais plutôt utiliser des unsigned char.

  3. #3
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    Les char sont par défaut signés sur la plupart des plate-formes, donc toute valeur supérieure à 127 devient "négative". Comme tu castes ça en int (signé lui aussi), le signe est conservé. Ce sont des bases d'arithmétique binaire.

    Lis des unsigned char et/ou affiche des unsigned int, tu ne devrais plus avoir le problème.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  4. #4
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Bonjour,

    Citation Envoyé par damien7307
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    char c[2];
    c[0] = ...
    c[1] = ...
    //...
    cout<< c <<endl;
    on obtient :

    BM°Î
    907256
    Il manque un '\0' à la fin du tableau de char. Donc std::cout va continuer à afficher la mémoire case par case en sortant du tableau juqu'à ce qu'il en trouve un.

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Citation Envoyé par damien7307 Voir le message
    Bonjour,

    Ceci est mon premier post sur ce forum. J'ai une année de pratique du C++.
    Bonjour et bienvenu,

    Citation Envoyé par damien7307 Voir le message
    Seulement, pourquoi est-ce que les deux caractères suivants sont négatifs ?
    Partons du fichier : celui-ci est une suite d'octets, qui si on les note hexadécimal, donne le flux suivant :
    0x42 0x4D 0xF8 0xD7 0x0D 0x00
    char est de type signé. Les deux premiers items sont positifs mais les deux suivants sont négatifs et utilisent le complément à 2 pour leur représentation. Si tu fais l'exercice de les convertir depuis leur représentation binaire vers la représentation décimal (en utilisant cette représentation des nombres négatifs avec le complément à 2), alors tu verras qu'ils représentent bien le -8 et le -41 qui te sont affichés.
    Quand on veut travailler avec des 'octets', on utilise en général unsigned char. Ce type n'est pas signé donc les 'octets' correspondent à ce que tu attends. Ainsi, tu peux changer ton code et regarder ce qu'il se passe :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ifstream fichier("test.bmp", ios::in | ios::binary);	
     
    	for(short int i = 0; i<5; ++i)
    	{
    		unsigned char uc;
    		fichier.get(uc);
     
    		cout<<(int) uc<<endl;
    	}
     
    fichier.close();
    [HS]Notes que je mets 'octets' entre guillemet car la norme ne te garantie pas qu'un char/unsigned char est sur un octet même si en pratique sur PC c'est toujours le cas. Considérons donc avec un peu d'abus de langage que unsigned char == octet [/HS]

    Citation Envoyé par damien7307 Voir le message
    Ne devrais-je pas avoir en sortie:
    ?
    1/Un tableau de char est traité - vieux reste du C - comme une chaîne de caractère et s'attend donc à un '\0' pour délimiter la fin de la chaîne. Or ton tableau de char ne contient que 2 éléments 'B' et 'M' ensuite il va chercher les valeurs à la suite qui contiennent des valeurs indéterminées et affichent donc n'importe quoi.
    2/Attention, tout comme précédemment la représentation binaire d'un char a une influence entre ce que tu crois lire et ce qui sera interprété par ton programme, tu ne peux lire un entier par la simple commande :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    f.read( (char *) &i, sizeof(int) );
    En effet, ici, le problème n'est plus au niveau de l'octet char/unsigned char mais de l'endianess. En d'autres termes, le nombre 907256 qui s'écrit en hexadécimal 0x000DD7F8 peut se retrouver selon la suite d'octets :
    0x00 0x0D 0xD7 0xF8
    ou
    0xF8 0xD7 0x0D 0x00
    Avec la lecture 'sauvage' f.read( (char *) &i, sizeof(int) );, i peut valoir 907256 (0x000DD7F8) ou 4174843136 (0xF8D70D00) car rien ne garantie que l'endianess utilisé pour écrire le fichier est le même que celui utilisé dans ton programme. Il faut pour cela vérifier l'endianess utilisé dans la norme BMP et opérer la conversion adéquate dans ton programme pour correspondre avec celle utilisée par les int de ton programme.
    Le problème d'endianess se pose pour tous les types entiers : short, int, long.

  6. #6
    Membre averti
    Profil pro
    Pilote / Testeur de tombereaux - Caterpillar
    Inscrit en
    Août 2008
    Messages
    28
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Pilote / Testeur de tombereaux - Caterpillar
    Secteur : Bâtiment Travaux Publics

    Informations forums :
    Inscription : Août 2008
    Messages : 28
    Par défaut Résolu
    Merci "3DArchi" pour votre réponse claire et complète. Mon problème est résolu. Il me reste encore du travail avant de bien maitriser ce language.

    En effet l'erreur du '\0' est trop bête, j'aurais dû y penser...

    Merci également aux autres pour leur réponse

    Damien.

  7. #7
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Citation Envoyé par 3DArchi Voir le message

    char est de type signé.
    Petit détail : char peut être signé ou non signé, c'est au choix de la plate-forme. Unisgned char est non signé, signed char est signé.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  8. #8
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Petit détail : char peut être signé ou non signé, c'est au choix de la plate-forme. Unisgned char est non signé, signed char est signé.
    Me coucherais moins bête ce soir.

  9. #9
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Par défaut
    Citation Envoyé par JolyLoic Voir le message
    Petit détail : char peut être signé ou non signé, c'est au choix de la plate-forme. Unisgned char est non signé, signed char est signé.
    Oui, c'est dépendant de la plate-forme (ou du réglage par défaut du compilateur, c'est notamment le cas avec Visual). Cependant, c'est souvent signé par défaut, et dans le doute, il vaut mieux systématiquement préciser "signed" ou "unsigned" si la notion de signe est importante dans le traitement.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Lecture fichier Word et remplacement paramètre
    Par Pfeffer dans le forum C++Builder
    Réponses: 4
    Dernier message: 21/02/2005, 17h30
  2. [ifstream] pb lecture fichier non sequentielle
    Par bludo dans le forum SL & STL
    Réponses: 3
    Dernier message: 10/02/2005, 21h30
  3. [LG]probleme lecture fichier
    Par yp036871 dans le forum Langage
    Réponses: 2
    Dernier message: 28/01/2004, 19h22
  4. [LG]Probleme lecture fichier file of ....
    Par John_win dans le forum Langage
    Réponses: 11
    Dernier message: 11/11/2003, 18h53
  5. [langage] prob lecture fichier .txt
    Par martijan dans le forum Langage
    Réponses: 3
    Dernier message: 16/07/2003, 11h08

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo