Nie czytaj bezpośrednio w struct z pliku! Pakowanie może być inne, trzeba skrzypce z pakietem pragma lub podobnymi kompilatorami. Zbyt niewiarygodne. Wielu programistów ucieka od tego, ponieważ ich kod nie jest skompilowany w wielu architekturach i systemach, ale to nie znaczy, że jest to w porządku!
Dobrym alternatywnym podejściem jest odczytanie nagłówka, cokolwiek, do bufora i parsowanie od trzech, aby uniknąć narzutu we/wy w operacjach atomowych, takich jak czytanie 32-bitowej liczby całkowitej bez znaku!
char buffer[32];
char* temp = buffer;
f.read(buffer, 32);
RECORD rec;
rec.foo = parse_uint32(temp); temp += 4;
rec.bar = parse_uint32(temp); temp += 4;
memcpy(&rec.fooword, temp, 11); temp += 11;
memcpy(%red.barword, temp, 11); temp += 11;
rec.baz = parse_uint16(temp); temp += 2;
Deklaracja parse_uint32 będzie wyglądać następująco:
uint32 parse_uint32(char* buffer)
{
uint32 x;
// ...
return x;
}
To bardzo proste abstrakcji, to nie ma żadnych dodatkowych kosztów w praktyce aktualizować wskaźnik, a także:
uint32 parse_uint32(char*& buffer)
{
uint32 x;
// ...
buffer += 4;
return x;
}
Późniejszy formularz umożliwia czystszy kod do analizowania bufora; wskaźnik jest automatycznie aktualizowany podczas analizowania z wejścia.
Podobnie mogłoby memcpy mieć pomocnika, coś jak:
void parse_copy(void* dest, char*& buffer, size_t size)
{
memcpy(dest, buffer, size);
buffer += size;
}
Piękno tego rodzaju rozwiązania jest to, że można mieć nazw „LITTLE_ENDIAN” i „big_endian”, to może to robić w Kod:
using little_endian;
// do your parsing for little_endian input stream here..
łatwo przełączać kolejność bajtów dla tego samego kodu, choć rzadko potrzebnych funkcji .. formatów plików zwykle mają ustaloną kolejność bajtów w każdym razie.
NIE zamieniaj tego na klasy za pomocą metod wirtualnych; po prostu dodać narzut, ale czuć się swobodnie, jeśli tak pochylona:
little_endian_reader reader(data, size);
uint32 x = reader.read_uint32();
uint32 y = reader.read_uint32();
Przedmiotem czytelnik będzie oczywiście być tylko cienka owijka wokół wskaźnika. Parametr wielkości będzie służył do sprawdzania błędów, jeśli jest. Niezupełnie obowiązkowe dla interfejsu per se.
Zauważ, jak wybór tu nie-endianess został zrobiony w CZASIE KOMPILACJI (ponieważ tworzymy obiekt little_endian_reader), więc wywołujemy metodę wirtualną narzut bez szczególnego powodu, więc nie podjąłbym tego podejścia. ;-)
Na tym etapie nie ma prawdziwego powodu, aby zachować strukturę plików w niezmienionej postaci, można uporządkować dane według własnych upodobań i niekoniecznie przeczytać je w jakiejkolwiek konkretnej strukturze; w końcu to tylko dane. Kiedy czytasz pliki takie jak obrazy, tak naprawdę nie potrzebujesz nagłówka dookoła ... powinieneś mieć swój kontener obrazu, który jest taki sam dla wszystkich typów plików, więc kod do odczytania określonego formatu powinien po prostu odczytać plik, zinterpretować i sformatować ponownie dane & przechowaj ładunek. =)
Mam na myśli, czy to wygląda skomplikowanie?
uint32 xsize = buffer.read<uint32>();
uint32 ysize = buffer.read<uint32>();
float aspect = buffer.read<float>();
Kod może wyglądać tak ładnie i być naprawdę niewielki! Jeśli kolejność bajtów jest taka sama dla pliku i architektury kod jest kompilowany do The innerloop może wyglądać następująco:
uint32 value = *reinterpret_cast<uint32*>)(ptr); ptr += 4;
return value;
To może być nielegalne w niektórych architekturach, tak że optymalizacja może być zły pomysł i używać wolniej, lecz bardziej solidne podejście:
uint32 value = ptr[0] | (static_cast<uint32>(ptr[1]) << 8) | ...; ptr += 4;
return value;
na x 86, który może kompilacji do bswap lub mov, która jest stosunkowo niska napowietrznych jeśli sposób wstawiane; kompilator wstawił węzeł "przenieś" do kodu pośredniego, nic ponadto, co jest dość wydajne. Jeśli wyrównanie jest problemem, cała sekwencja read-shift-lub może zostać wygenerowana, outch, ale nadal nie jest zbyt shabby. Funkcja porównania może pozwolić na optymalizację, jeśli przetestuje adres LSB i sprawdzi, czy można użyć szybkiej lub wolnej wersji analizy. Ale oznaczałoby to karę za test w każdym czytaniu. Może nie był wart wysiłku.
O, prawda, czytamy HEADERS i takie tam, nie sądzę, że jest to wąskie gardło w zbyt wielu aplikacjach. Jeśli jakiś kodek robi naprawdę WILGOTNY PUNKT, znowu odczytanie bufora tymczasowego i dekodowanie z niego jest dobrze przygotowane. Ta sama zasada. Nikt nie czyta bajt-w-czasie z pliku podczas przetwarzania dużej ilości danych. Tak naprawdę, widziałem ten rodzaj kodu bardzo często, a zwykła odpowiedź "dlaczego to robisz" jest taka, że systemy plików blokują odczyty i że bajty pochodzą z pamięci, tak czy inaczej, ale przechodzą przez głęboki stos wywołań co jest dużym obciążeniem dla uzyskania kilku bajtów!
Jeszcze raz napisz kod parsera i użyj zillion razy -> epicka wygrana.
Odczytanie bezpośrednio do struktury z pliku: NIE ROZPOCZNIJ!
Nie pytałeś o nie, ale jeszcze jedną rzeczą do rozważenia przy pracy z tego rodzaju starszym kodem są bitfields. Kolejność pakowania bitoftów może być zarówno zależna od kompilatora, jak i platformy i niezwiązana z endianowością procesora. – Dan