2011-01-08 1 views
8

J'essaie de lire le contenu du fichier PNG.comment transformer un tableau de char en un seul nombre entier?

Comme vous le savez peut-être, toutes les données sont écrites en 4 octets dans les fichiers png, à la fois le texte et les nombres. donc, si nous avons le numéro 35234, c'est comme ceci: [1000] [1001] [1010] [0010].

mais parfois les nombres sont plus courts, donc les premiers octets sont zéro, et quand je lis le tableau et le jette de char * à integer, je reçois un mauvais numéro. par exemple [0000] [0000] [0001] [1011] parfois les nombres sont mal interprétés comme des nombres négatifs et des fois comme zéro!

laissez-moi vous donner un exemple intuitif:

char s_num[4] = {120, 80, 40, 1}; 

int t_num = 0; 

t_num = int(s_num); 

Je voudrais bien expliquer mon problème! Comment est-ce que je peux jeter de telles matrices dans une valeur simple d'entier?

ok ok ok, laissez-moi changer mon code pour l'expliquer mieux:

char s_num[4] = {0, 0, 0, 13}; 
int t_num; 


t_num = *((int*) s_num); 
cout << "t_num: " << t_num << endl; 

ici, nous devons obtenir 13 le résultat, ok? mais encore une fois avec cette nouvelle solution, la réponse est fausse, vous pouvez tester sur vos ordinateurs! je reçois ce numéro: 218103808 ce qui est absolument faux!

+1

Votre machine est big-endian ou little-endian .... lire ceci: http://en.wikipedia.org/wiki/Endianness#Big-endian – Nawaz

+1

Comment lancez-vous en passant? poster du code .. – Nawaz

+1

[1000] [1001] [1010] [0010] est deux octets, pas quatre. – TonyK

Répondre

10

Vous transtypez (char *) en (int). Ce que vous devez faire est de jeter à pointeur en entier, soit

t_num = *((int*) s_num)); 

Mais vraiment, vous devez extraire votre code dans sa propre fonction et assurez-vous que:

  1. boutisme est correct
  2. sizeof(int) == 4
  3. Utiliser les distributions C++ (c.-à-d. static, dynamic, const, reinterpret)
+0

que signifie le même résultat? peut-être devriez-vous expliquer, pourquoi vous vous attendez à ce que cela donne 241 ... – Axel

+0

Oh ... ce que vous voulez, c'est juste pour résumer les valeurs de l'octet de vos 4 caractères. Cela donnerait 241. La réponse de Macieks le fait. Mais est-ce vraiment ce que vous voulez faire? Cela me semble étrange (pas le code, j'essaie de comprendre votre problème). – Axel

+0

ok, c'est vrai. poooooooooof – sepisoad

6

En supposant une machine peu endian avec un entier de 32 bits, vous pouvez le faire:

char s_num[4] = {0xAF, 0x50, 0x28, 0x1}; 
int t_num = *((int*)s_num); 

Pour le casser en plusieurs étapes:

  1. s_num est un tableau, qui peut être interprété comme un pointeur vers son premier élément (char* ici)
  2. Cast s_num à int* en raison de (1) - il est OK pour lancer des pointeurs
  3. Accès l'entier pointée par le pointeur de coulée (de déréférencement)

Pour avoir 0xAF comme octet de poids faible de l'entier. Fuller exemple (code C):

#include <stdio.h> 

int main() 
{ 
    char s_num[4] = {0xAF, 0x50, 0x28, 0x1}; 
    int t_num = *((int*)s_num); 

    printf("%x\n", t_num); 
    return 0; 
} 

Prints:

12850af 

Comme prévu.

Notez que cette méthode n'est pas trop portable, car elle suppose l'endianness et la taille de l'entier. Si vous avez une tâche simple à effectuer sur une seule machine, vous pouvez vous en sortir, mais pour une qualité de production quelque chose, vous devrez tenir compte de la portabilité.

En outre, dans le code C++, il serait préférable d'utiliser reinterpret_cast au lieu de la distribution de style C.

2

Je trouve que l'utilisation de l'ensemble de bits std est la façon la plus explicite de faire des conversions (en particulier le débogage).Ce qui suit n'est peut-être pas ce que vous voulez dans votre code final (trop verbeux peut-être) - mais je trouve ça génial d'essayer de comprendre exactement ce qui se passe.

http://www.cplusplus.com/reference/stl/bitset/

#include <bitset> 
#include <iostream> 
#include <string> 

int 
main (int ac, char **av) 
{ 

    char s_num[4] = {120, 80, 40, 1}; 
    std::bitset<8> zeroth = s_num[0]; 
    std::bitset<8> first = s_num[1]; 
    std::bitset<8> second = s_num[2]; 
    std::bitset<8> third = s_num[3]; 

    std::bitset<32> combo; 
    for(size_t i=0;i<8;++i){ 
    combo[i]  = zeroth[i]; 
    combo[i+8] = first[i]; 
    combo[i+16] = second[i]; 
    combo[i+24] = third[i]; 
    } 
    for(size_t i = 0; i<32; ++i) 
    { 
     std::cout<<"bits ["<<i<<"] ="<<combo.test(i)<<std::endl; 
    } 
    std::cout<<"int = "<<combo.to_ulong()<<std::endl; 
} 
+0

+1 pour utiliser 'std :: bitset <>'; – Nawaz

-1

Saviez-vous que int est dans le débordement C++ après la valeur 32767'th? Cela expliquerait votre nombre négatif pour 35234.

La solution consiste à utiliser un type de données qui peut gérer les plus grandes valeurs. Voir l'article de dépassement d'entier pour plus d'informations:

http://en.wikipedia.org/wiki/Integer_overflow

MISE À JOUR: Je ne pense pas écrit ce que nous vivons tous dans le monde moderne où 32 bits et 64 bits machines existent et s'épanouissent !! Le débordement pour int est en fait beaucoup plus grand que mon énoncé original.

+3

Quelle machine avez-vous qui déborde un int sur 32767? –

+0

pas nécessairement ainsi. Beaucoup d'implémentations en C++ ont un 'int' de 32 bits ou même de 64 bits qui ne débordera pas après 32767. –

+0

@Philip:" Many ... "c'est le moins qu'on puisse dire. Il serait difficile d'en trouver un où il est plus petit que 32 bits ces jours-ci, en dehors du domaine intégré bien sûr. –

0

La conversion est bien effectuée, parce que vous ne résumez pas ces valeurs mais les attribuez comme une valeur unique. Si vous voulez les résumer, vous devez le faire manualy:

int i; 
for (i = 0; i<4; ++i) 
    t_num += s_num[i]; 
0

EDIT: il semble que vous ne voulez pas additionner les chiffres après tout. Laissant cette réponse ici pour la postérité, mais il ne répond probablement pas à la question que vous voulez poser.

Vous voulez résumer les valeurs, donc utiliser std::accumulate:

#include <numeric> 
#include <iostream> 

int main(void) { 
    char s_num[4] = {120,80,40,1}; 
    std::cout << std::accumulate(s_num, s_num+4,0) << std::endl; 
    return 0; 
} 

produit une sortie:

[email protected]:~/tmp$ g++ -ansi -pedantic -W -Wall foo.cpp -ofoo 
[email protected]:~/tmp$ ./foo 
241 
+0

Je ne suis pas sûr que c'est ce qu'il demande –

+0

@Eli je ne pense pas * il est * sûr de ce qu'il demande. Mais c'est le résultat que son exemple demande. –

+0

le même se produit quand j'ai utilisé votre solution – sepisoad

1

Réponse de Axel violates the strict aliasing rule, depuis au moins C++ 14. Donc, je poste cette réponse pour les futurs utilisateurs.


Outre les questions de endianness et de la taille, un moyen sûr est d'utiliser std::memcpy, à savoir

unsigned char s_num[4] = {13, 0, 0, 0}; 
// ^^^^^^^^    // ^^ fix endianness issue 
// use unsigned char to avoid potential issues caused by sign bit 

int t_num; 

std::memcpy(&t_num, s_num, 4); 
Questions connexes