Toon posts:

[C++] Struct uit bestand lezen.

Pagina: 1
Acties:

Verwijderd

Topicstarter
Ik wil een programma maken, dat handmatig een bitmap inleest en weergeeft. Ik ben nogal nieuw met c++, dus het volgende werkt niet zoals verwacht:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdlib.h>
#include <fstream.h>

typedef struct bmpheader {
        short id;
        int filesize;
};       

const char * filename = "bmp1.bmp";

int main(int argc, char *argv[]) {
  bmpheader header1;
  ifstream file (filename, ios::in|ios::binary);

//eerste voorbeeld
  file.seekg(0,ios::beg);
  file.read(&header1.id,sizeof(header1.id));
  file.read(&header1.filesize,sizeof(header1.filesize));
  cout << "ID: " << header1.id;
  cout << " Filesize: " << header1.filesize << endl;

//tweede voorbeeld
  file.seekg(0,ios::beg);
  file.read(&header1,sizeof(header1));
  cout << "ID: " << header1.id;
  cout << " Filesize: " << header1.filesize;

  file.close();
  system("pause");
  return 0;
}


Het probleem is dus dat als ik per element in mijn struct-type ga lezen, zoals het eerste voorbeeld het perfect werkt: (output "ID:19778 Filesize:1798"), zoals ook in het bestand staat.

Als ik nu dus in een keer de hele struct wil lezen, hij de variabelen in header1 niet goed inlaadt: (output "ID:19778 Filesize:0"). Ook bij meer variabelen in de header zal alleen de eerste (id) werken. Hoe komt dit? Wat doe ik verkeerd?

[ Voor 9% gewijzigd door Verwijderd op 22-05-2003 21:42 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

tip: gebruik [norml][code=c++]...[/code][/] voor highlighting

ten eerste: include <fstream> ipv <fstream.h>, dat is de standaard (en alle classes zitten nu in de std namespace)

De reden dat het waarschijnlijk niet werkt is alignment: de short is 2 bytes, en de int 4. Standaard wordt de int aligned op 4 bytes. De totale struct size wordt dus 8.
Welke compiler gebruik je?

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Topicstarter
Ik gebruik Dev-Cpp, en die gebruikt geloof ik een win32 port van gcc.

Nu snap ik eigenlijk nog steeds niet waarom struct 8 bytes groot is? Ik wil nu maar 6 bytes lezen, aangezien dat ook in mijn bmp staat.

Hoe moet ik dan toch de correcte data inlezen?

--

Met sizeof(header1) blijkt hij inderdaad 8 bytes te zijn

[ Voor 18% gewijzigd door Verwijderd op 22-05-2003 21:45 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
.oisyn schreef op 22 mei 2003 @ 21:38:
tip: gebruik [norml][code=c++]...[/code][/] voor highlighting

[nohtml]ten eerste: include <fstream> ipv <fstream.h>, dat is de standaard (en alle classes zitten nu in de std namespace)

De reden dat het waarschijnlijk niet werkt is alignment: de short is 2 bytes, en de int 4. Standaard wordt de int aligned op 4 bytes. De totale struct size wordt dus 8.
Welke compiler gebruik je?
Tip: Doe dat vooral als je als modje HTML rechten hebt :)

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
De correcte methode om bytes uit te lezen is met de code uit je eerste voorbeeld. Je compiler mag padding introduceren op elk punt waar hij denkt dat dat nuttig is. Een gangbare reden is dat integers op een adres N*4 moeten staan.

Als je geen padding zou hebben zou je geen arrays van bmpheader's kunnen hebben: De eerste int zou op adres A+2 staan, de tweede op A+8. Hooguit een van de twee is een veelvoud van 4.

Met 2 bytes padding hoef je alleen te zorgen dat de eerste bmpheader op een adres N*4 begint, dan kloppen alle ints in de array.

Op x86/Windows is een misalignment langzamer; op Unix/RISC is het typisch fataal.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Verwijderd

Topicstarter
Het vervelende is dus dat de header eigenlijk veel langer is, en dat als ik het variabele voor variabele moet inladen het nogal een gedoe is. Het is dus blijkbaar onmogelijk om de hele struct in een keer in te laden, aangezien die c compiler alle int locaties op 4 gaat afronden (als ik het goed snap?)

Eigenlijke struct:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct bmpheader {
        unsigned short id;
        unsigned int filesize;
        int reserved;
        unsigned int headersize;
        unsigned int width;
        unsigned int height;
        unsigned short planes;
        unsigned short bpp;
        unsigned int compression;
        unsigned int bitmapdatasize;
        unsigned int hresolution;
        unsigned int vresolution;
        unsigned int colors; 
        unsigned int importantcolors;
};

  • Juicy
  • Registratie: December 2000
  • Laatst online: 08:27
Zorg er ook even voor dat header1 is gealloceerd is.

-


  • hobbit_be
  • Registratie: November 2002
  • Laatst online: 04-07-2025
kijk ook naar #pragma pack als je compiler dat heeft. Overigens kun je je variablen zelf align declaren: ie eerste alle 32bitters. dan 16 -> 8.

moet zeggen dat ik daar nog nooit met fopen/fread ben tegengelopen... tja die 'c++ streams ook' :)

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

MSalters schreef op 22 May 2003 @ 22:19:
[...]

Tip: Doe dat vooral als je als modje HTML rechten hebt :)
oh, ik had de [nohtml] tag wel gebruikt, maar was 'm vergeten af te sluiten :P

[ Voor 3% gewijzigd door .oisyn op 23-05-2003 10:39 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

hobbit_be schreef op 22 May 2003 @ 22:40:
kijk ook naar #pragma pack als je compiler dat heeft. Overigens kun je je variablen zelf align declaren: ie eerste alle 32bitters. dan 16 -> 8.
dat werkt met MSVC++ ja, en voor gnu is er ook een variant, maar zoals MSalters al zei, er zijn processoren waar een verkeerde alignment niet eens mogelijk is.

De netste methode is dan ook om member voor member die dingen te extracten uit de file, zoals in de topicstarter's eerste voorbeeld
moet zeggen dat ik daar nog nooit met fopen/fread ben tegengelopen... tja die 'c++ streams ook' :)
Dat heeft er niets mee te maken. De read methode van fstream leest ook gewoon de bytes uit de file en plaatst ze ergens in het geheugen. Hij heeft verder geen weet van de contents van de structure

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • JoostBaksteen
  • Registratie: December 2000
  • Laatst online: 27-03 19:32
Verwijderd schreef op 22 mei 2003 @ 22:34:
Het vervelende is dus dat de header eigenlijk veel langer is, en dat als ik het variabele voor variabele moet inladen het nogal een gedoe is. Het is dus blijkbaar onmogelijk om de hele struct in een keer in te laden, aangezien die c compiler alle int locaties op 4 gaat afronden (als ik het goed snap?)

Eigenlijke struct:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct bmpheader {
        unsigned short id;
        unsigned int filesize;
        int reserved;
        unsigned int headersize;
        unsigned int width;
        unsigned int height;
        unsigned short planes;
        unsigned short bpp;
        unsigned int compression;
        unsigned int bitmapdatasize;
        unsigned int hresolution;
        unsigned int vresolution;
        unsigned int colors; 
        unsigned int importantcolors;
};
Wat jij bedoelt is het inladen van x bytes uit bestand y en dan in een struct te zetten , toch?

pseudo:
C++:
1
2
3
4
bmpheader header;

buffer = readBmpHeader();
header = buffer;


en dat hij dan meteen die hele struct vult, eerst 4 bytes in de short en zo alle variabelen afwerken.

Dit kan volgens mij niet in c++

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

joostbaksteen schreef op 23 mei 2003 @ 11:15:
Dit kan volgens mij niet in c++
Dat kan wel, alleen door alignment gaat het dus fout. Als je een struct wegschrijft en dan inleest werkt het wel goed: de alignment wordt namelijk ook weggeschreven.

En anders zul je het moeten doen met compiler-specifieke directives, zoals #pragma pack in MSVC++

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 21-08 17:14
joostbaksteen schreef op 23 May 2003 @ 11:15:

Wat jij bedoelt is het inladen van x bytes uit bestand y en dan in een struct te zetten , toch?

pseudo:
C++:
1
2
3
4
bmpheader header;

buffer = readBmpHeader();
header = buffer;


en dat hij dan meteen die hele struct vult, eerst 4 bytes in de short en zo alle variabelen afwerken.

Dit kan volgens mij niet in c++
Het kan wel, onder een aantal verschillende condities

*De struct moet een C-compatible struct/class zijn (aka POD, Plain Old Data), en de gelezen data moet dezelfde padding hebben op disk. Dat kan als je bijv. zo'n zelfde structure eerst hebt weggeschreven

* De struct is via generative programming opgebouwd, waarbij de read-functie meegebouwd wordt.

In het laatste voorbeeld krijg je dan een class als

typedef member< long,
member< short,
member< char[20]
> > > myStruct;

myStruct S;
S.read ( "File.bin");
long l = S(0);

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein

Pagina: 1