Odczyt pliku binarnego - złożona struktura

0

Witam,
mam następujące pytanie:
mam 2 klasy typu:

class Inner
{
float a,b;
}
class Main
{
int t;
Inner inner;
}

oraz plik binarny, w którym dane są zapisane w następujący sposób: int, float, float.
Moje pytanie brzmi: czy dane z owego pliku binarnego można ładować bezpośrednio do klasy Main? Tzn. czy klasa/struktura może zwierać wewnątrz kolejną strukturę (czy to jakoś nie przeszkadza)?
Czy kolejność zmiennych w klasie(ach) będzie zawsze zachowana? Tzn. czy kompilator nie zmieni mi np. kolejności t z inner albo b z a?

Z góry dziękuję za pomoc!

0

No można tak jak Ty to chcesz zrobić.

A nie prościej tak:

struct FileInsides {
   int i;
   float f1;
   float f2;
} /* __attribute__((packed));  niekoniecznie, ale moze byc to przydatne */

Generalnie Twój problem nazywa się w stylu "data alignment" bądź "struct alignment", szukaj po tych hasłach.

1

@Wielki Kot kolejności nie zmieni, ale jeśli któraś z tych klas będzie miała składniki wirtualne to sie posypie. Poza tym chyba może się zrobić jakieś wyrównanie, ale tego bałbym się bardziej dla pól bitowych.

0

@Shalom, @Wielki Kot tutaj akurat wyrównanie raczej nie będzie mieć znaczenia, bo typowo to i int i floaty mają długość 32b. Ale już przy wsadzeniu tam char mógłby być problem:
http://ideone.com/S4IYB3

No i na pewnych architekturach (na pewno na ARM) zły alignment może spowodować różne dziwne rzeczy (na ARM wywołuje jakiegoś typu sprzętowy wyjątek, nie pomnę który).

0

Dziękuję za odpowiedzi!
Mam jeszcze pytanie czy w 64 bitowym programie jest wyrównanie do 8 bajtów czy również do 4? Tzn. czy w moim przykładzie struktura nie zostanie zmieniona czy zostanie powiększona do 16 bajtów?
pozdrawiam

0

Sprawdź ;)

0

Więcej przykładów: http://ideone.com/jrZ9Aa

0
alagner napisał(a):

Sprawdź ;)

Done ;-).
W VS 2013 zaokrągla do 4 bajtów, ale czy tak będzie zawsze?

0

Eksperymentalnie to najprościej byłoby Ci wpisać do pól znane wartości i podejrzeć debugerem gdzie zaczyna się padding. Ale nie pomogę przy VS, używam gcc.
Ew. poszukaj tutaj
https://msdn.microsoft.com/en-us/library/71kf49f1.aspx

0

Ok, dzięki za pomoc!
pozdrawiam ;-)

0

Naszła mnie jeszcze taka jedna refleksja:
Jeśli mam jakąś strukturę, którą nie jest podzielna przez 4 np.

struct {char a,b; int c;} //6 bajtów

To kompilator wyrówna ją do 8 bajtów.

Natomiast jeśli zapiszę dane takiej struktury do pliku binarnego to zapis również będzie po 8 bajtów?! A jeśli tak to odczytując dane również powinienem odczytywać po 8 bajtów (tak jak zapisałem) - czyli nie ma potrzeby korzystania z żadnych #pragma pack(push, 1) lub innych (analogicznych) mechanizmów?!

Czy może ten problem dotyczy tylko odczytu i trzeba o to zadbać?

Bardzo proszę jeszcze o pomoc w tej kwestii.

0

Problem polega na tym, że ułożenie elementów w strukturze lub klasie zależy od tego jak kompilator jest ustawiony.
najcześciej kompilator próbuje tak ułożyć elementy, by zachowując ich kolejność uzyskać jak największą wydajność.
Efekt jest taki, że int może być równany do słowa (dw bajty), lub podwójnego słowa.
Dlatego taki hurtowy zapis binarny jest obciążony dużym ryzykiem i trzeba się przy nim bardzo pilnować!
Jeśli stosujesz rożne kompilatory/platformy sprawa jeszcze bardziej się komplikuje. Z tego powodu unika się takiego zapisu i odczytu danych.
Jak do tego dołożyć problemy z utrzymaniem formatu danych to nie ma co się dziwić, że do powszechnego użytku wszedł xml i JSon (+zip).

0

Dziękuję @MarekR22 za odpowiedź.

MarekR22 napisał(a):

Problem polega na tym, że ułożenie elementów w strukturze lub klasie zależy od tego jak kompilator jest ustawiony.
najcześciej kompilator próbuje tak ułożyć elementy, by zachowując ich kolejność uzyskać jak największą wydajność.
Efekt jest taki, że int może być równany do słowa (dw bajty), lub podwójnego słowa.
Dlatego taki hurtowy zapis binarny jest obciążony dużym ryzykiem i trzeba się przy nim bardzo pilnować!

Ale, jeśli dobrze rozumiem, jeśli ten sam program będzie zapisywał dane do pliku i je później odczytywał tj. skompilowany tym samym kompilatorem z takimi samymi ustawieniami, to rozumiem, że jest to bezpieczne? Bo wówczas zarówno przy zapisie jak i odczycie klasa będzie tak samo wyrównana i odczyt powinien przypisać tym samym zmiennym te same wartości (nawet jak będą w zmienionej kolejności/wielkości)?

Ogólnie to problem jest tego typu, że piszę aplikację, która będzie korzystać ze stosunkowo dużego zbioru danych (około 1 GB) i chcę je wszystkie ładować do RAM-u w miarę szybko. Z pliku tekstowego trwa to stosunkowo długo i chcę to przyśpieszyć zapisując dane do pliku binarnego.

Moja klasa zapisująca i odczytująca dane na chwilę obecną wygląda następująco:

		vector<MyData> m_data;
		void SaveDataBinary(string filename)
			{
			size_t tmp_size = m_data.size();
			ofstream fout(filename, ios::binary);
			fout.write((char *)(&tmp_size), sizeof(tmp_size));
			fout.write((char *)(&m_data[0]), sizeof(MyData)*tmp_size);
			fout.close();
			}

		void LoadDataBinary(string filename)
			{
			size_t tmp_size = 0;
			ifstream fin(filename, ios::binary);

			fin.read((char *)(&tmp_size), sizeof(size_t));
			m_data.resize(tmp_size);

			fin.read((char *)(&m_data[0]), sizeof(MyData)*tmp_size);

			}

Jeśli jest taka możliwość to również proszę o komentarz, czy jest ok ;-).

1 użytkowników online, w tym zalogowanych: 0, gości: 1