Czy istnieje jakieś niebezpieczeństwo podczas zapisywania surowych bajtów do pliku? [Zamknięte]

12

Pracuję nad problemem w Programming Pearls - w szczególności implementacją programu, który sortuje plik zawierający najwyżej 10 000 000 liczb całkowitych (kolumna 1, problem 3). Ponieważ książka nie określa, w jaki sposób dane powinny być przechowywane w pliku, rozważam przechowywanie liczb całkowitych jako nieprzetworzonych bajtów (istnieją inne ograniczenia, które sprawiają, że nieprzetworzone bajty są dobrą opcją). Nigdy wcześniej nie pracowałem na tak niskim poziomie, więc chcę wiedzieć, czy jest coś niebezpiecznego, na co powinienem uważać. Czy muszę się martwić, że przypadkowo użyję jakiejś sekwencji końca pliku, gdy na przykład zapisuję surowe bajty do pliku?

Edytować:

Teraz zdaję sobie sprawę, jak szerokie było moje pytanie. Naprawdę miałem na myśli problemy bardziej katastrofalne, takie jak przypadkowe zastąpienie innych plików na dysku. Przepraszam, nie byłem jaśniejszy.

Drake Sobania
źródło
6
Zauważ, że Programowanie pereł to bardzo stara książka; możesz z łatwością odczytać wszystkie liczby całkowite 10 ^ 7 do pamięci na nowoczesnym komputerze stacjonarnym, zrób to i napisz to ponownie. Aby uzyskać oryginalny punkt tego rozdziału, ogranicz ilość czytanego tekstu w dowolnym momencie do ułamka całkowitej liczby. Lub zwiększ rozmiar pliku do około 10 ^ 10 liczb całkowitych.
Caleb
3
Właściwie, kiedy słyszę słowo „niebezpieczne”, myślę o rzeczach, które powodują, że mój komputer eksploduje, usuwa moje konta bankowe lub coś w tym rodzaju. Przypuszczam, że najprawdopodobniej można bezpiecznie założyć, że dopóki twój program nie jest używany do sterowania Airbusem lub elektrownią, nic naprawdę „niebezpiecznego” nie wydarzy się, gdy wypróbujesz to, co masz na myśli.
Doc Brown
2
@delnan Wiele lat temu, kiedy mit o postaci EOF był w modzie, przypominam sobie systemy ochrony przed kopiowaniem, które były oparte na „kopiowaniu do postaci EOF”, co robiło wiele ówczesnych programów do kopiowania. Niektóre programy umieszczałyby dodatkowe dane, które sprawdzałyby po znaczniku EOF powiązanego pliku tekstowego, ale przed przydzielonym końcem pliku. Program do kopiowania nie skopiowałby dodatkowych danych potwierdzających czystą instalację ... ahh ... nostalgia.
zagrożenie? Jak w „Czy mój komputer wysadzi się, jeśli to zrobię”? Nie.
jwenting

Odpowiedzi:

11

Jedynym niebezpieczeństwem, na które napotkasz, jest niewielka kontra duża endianizm (niezależnie od tego, czy bajt o największym lub najmniejszym znaczeniu jest zapisywany jako pierwszy). Jeśli jednak pozostaniesz w tym samym środowisku, nie będzie problemu. oprócz ogólnego zapewnienia pisania / parsowania w obie strony.

System plików jest zaprojektowany do obsługi dowolnej sekwencji bajtów.

maniak zapadkowy
źródło
2
+1 za ostatnią linię. Nie jestem pewien, czy duży / mały problem jest jedynym problemem - na przykład OP może się mylić, gdzie są granice między liczbami całkowitymi. Ale i tak dobra odpowiedź.
Caleb
27

Nie, w rzeczywistości tak działa wiele formatów plików. Typowe przykłady takich plików binarnych to obrazy i pliki muzyczne / audio.

Aby zachować integralność pliku i odczytanych z niego danych, należy przestrzegać następujących wskazówek:

  • Zawsze otwieraj plik (odczyt lub zapis) w tym samym trybie: tekstowym lub binarnym. Podstawowa różnica polega na tym, że tryb tekstowy dba o znaki nowej linii i może „odciążyć” znaki nowej linii podczas czytania pliku (w zależności od używanej biblioteki). Tryb tekstowy może także wykonywać tłumaczenia w standardzie Unicode, które prawdopodobnie zadławią dane inne niż Unicode.
  • Podczas odczytywania danych nieciągłych pamiętaj, aby czytać przy użyciu tego samego typu danych, co podczas pisania. Na przykład, jeśli pierwsze cztery bajty pliku to opisowa liczba całkowita, pamiętaj, aby czytać i pisać za pomocą metody, która przyjmuje / zapewnia liczbę całkowitą, aby zapewnić, że jest ona traktowana konsekwentnie. Ten sam typ danych może mieć różny rozmiar na różnych komputerach, a mieszanie typów danych na tej samej maszynie może również zmienić znaczenie danych (np. Interpretowanie bitu w środku dłuższej liczby całkowitej jako bitu znakowego).
  • Endianness: jeśli używana biblioteka nie radzi sobie z tym konsekwentnie, być może będziesz musiał poradzić sobie z tym samodzielnie. Na przykład Java zawsze używa sieciowej kolejności bajtów (big endian) dla typów wielobajtowych. C i C ++ używają tego, co zdecydował implementator biblioteki, zwykle taki sam jak procesor (mały endian w Intelu, duży endian w większości innych). Jeśli jest to szybkie ćwiczenie na jednym systemie, nie jest to tak ważne, ale nadal dobrym nawykiem jest zwracanie na to uwagi i kodowanie go w razie potrzeby.

Szczegółowe informacje będą się różnić w zależności od frameworka, platformy i języka, ale powinno to obejmować podstawowe „gotchas” z I / O pliku.


źródło
3
Dodatkowa uwaga dla danych nieciągłych: upewnij się, że używasz stałej liczby bajtów dla każdego typu. W C i C ++ an intmoże zawierać od 2 do 8 lub więcej bajtów (tak naprawdę oktetów).
Bart van Ingen Schenau
Jest to domyślnie włączone do mojego drugiego punktu, np. Liczba całkowita 32 w. 64 bit. Byłyby to różne typy danych.
Możesz wyrazić to wyraźnie. Nie jest oczywiste, że intna dwóch różnych komputerach można uznać za różne typy danych.
Bart van Ingen Schenau
9

Oprócz wszystkich wspomnianych już gotchas, jeśli tworzysz nowy format pliku binarnego, a nie odczytujesz i zapisujesz dane w istniejącym formacie, absolutnie niezbędne jest dołączenie nagłówka pliku : bloku danych na samym początku pliku, który jednoznacznie identyfikuje format pliku i rejestruje wszelkie wymagane metadane.

Dobre nagłówki plików obejmują co najmniej trzy rzeczy:

  • Magiczna liczba ”, co najmniej cztery bajty. Magiczna liczba MUSI rfc2119 być pierwszym N bajtami w pliku, MUSI nigdy nie być używana w żadnym innym formacie pliku, który można wykopać, i MUSI zawierać co najmniej jeden bajt, który nie jest drukowalnym znakiem ASCII. Zobacz specyfikację PNG, aby dowiedzieć się, jak zaprojektować naprawdę dokładną liczbę magiczną. Zobacz kod źródłowy file(1)polecenia, aby znaleźć bazę istniejących magicznych liczb, która jest tak obszerna, jak to tylko możliwe.

    Magiczna liczba polega na jednoznacznym oznaczeniu pliku wewnątrz pasma jego formatem. Jeśli nie podasz magicznej liczby lub nie jest to pierwsza rzecz w pliku, ryzykujesz, że programy błędnie zidentyfikują Twój plik jako inny typ pliku, co prowadzi do utraty danych, wykrycia wirusów i innych tego typu katastrofy.

  • Wskazanie wersji formatu pliku. Nawet jeśli uważasz, że nigdy nie będziesz musiał radykalnie zmieniać formatu pliku, utwórz kolejne dwa bajty po magicznej liczbie 00 00i udokumentuj, że jest to 16-bitowy numer wersji z pewną dokładnością (cokolwiek chcesz, ale wybierz jeden i trzymaj się go w całym pliku ) i zostanie zwiększony, jeśli znaczenie kolejnych danych zmieni się radykalnie. Twoje przyszłe ja będzie ci wdzięczne.

    (Specyfikacja PNG podąża tutaj inną drogą, określając, że formaty porcji są zamrożone i że wszystkie przyszłe zmiany formatu przyjmą formę nowych typów porcji. Jest to również ważne, ale zalecam proste podejście magicznej liczby + numeru wersji dla początkujący do binarnego przetwarzania danych. Ludzie, którzy zaprojektowali PNG, czerpali z kolektywnych dekad doświadczeń z formatami obrazów.)

  • Pewien mechanizm osadzania dowolnych metadanych w pliku. Może to być tak proste, że kolejne dwa bajty to 16-bitowe przesunięcie od końca nagłówka do początku rzeczywistych danych, przy czym wszystko pomiędzy należy interpretować jako pary klucz-wartość UTF-8 a la RFC 822 (to znaczy „ Tag: value\n” - jeśli wybierzesz tę trasę, zalecam nie zezwalanie na składanie długich linii). Ponownie PNG jest znacznie mądrzejszy.

zwol
źródło
Nie musisz tworzyć własnego formatu pliku ... po prostu przechowuj dane jako obraz. Może być konieczna zmiana wymiarów (np. 10k x 1k), aby była obsługiwana. Lub możesz użyć FITS . Jeśli Twoje dane są bardziej złożone niż tylko jedna tablica, możesz użyć HDF , CDF lub NetCDF .
Joe
Sugerowałbym, żeby to było proste. Wystarczy 256 różnych wersji, a jeśli nie, można opracować dodatkowe wersje jako podwersje wersji 255. Podobnie w przypadku metadanych wystarczy dodać je do wersji, gdy są rzeczywiście potrzebne. @Joe Image ??? Unikasz potencjalnego pomyłki formatu, uprzedzając wszystkich wcześniej!
maaartinus
@maaartinus Ustawienie pola bajtów na dwa bajty zmusza projektanta formatu do zaakceptowania endianizmu z góry. Miejsce na metadane powinno zawsze być w wersji 0 formatu binarnego, w przeciwnym razie skończysz z okropnymi kłopotami, takimi jak ID3. Mam wiele sympatii do logiki specyfikacji PNG dotyczącej rozszerzalności za pomocą nowych typów porcji zamiast błędów wersji formatu. Jednak pliki o strukturze fragmentów przynoszą wiele własnych problemów, dlatego waham się polecić je w prostych przypadkach. I był kuszony polecić HDF jako ogólny format, który zajmowali z dużą ilością już tych problemów.
zwolnić
2

Różne architektury mają różne reprezentacje liczb całkowitych. Głównym ryzykiem tutaj jest zapisanie bajtowej reprezentacji liczby całkowitej na maszynie A, a następnie próba odczytania tego z powrotem i zinterpretowania zawartości jako liczb całkowitych na maszynie B. Jeśli maszyny A i B mają różne rozmiary liczb całkowitych i / lub inny endian , „ Najprawdopodobniej spowoduje niezdefiniowane zachowanie (np. w C) lub wyjątek.

Ponieważ jest to tylko przykład programowania, a nie „prawdziwy” program, nie jest to tak naprawdę problemem. Gdyby to był prawdziwy program, rozwijanie własnego formatu binarnego specyficznego dla aplikacji zwykle nie jest dobrym pomysłem; istnieją lepsze rozwiązania, takie jak SQLite lub oparte na łańcuchach formaty szeregowania, takie jak JSON, YAML, XML itp. W przypadku pojedynczych wartości wystarczyłoby przekształcenie go w ciąg; w przypadku prostych list można zapisać jeden ciąg w wierszu i po prostu podzielić dane wejściowe na nowe wiersze po ponownym odczytaniu.

Doval
źródło
Zgadzam się ogólnie, ale JSON lub XML znacznie zwiększyłyby rozmiar pliku zawierającego 10 ^ 7 liczb. Ponadto są one ogólnie odczytywane i analizowane jednocześnie, ale omawiany rozdział dotyczy sortowania plików zawierających więcej danych, niż można zmieścić w dostępnej pamięci.
Caleb
To zależy od tego, co robisz. Czasami wydajność SQL-a-roll-your-own jest znacząca. Ostatnim razem, gdy to zrobiłem, miałem małe płyty i była duża szansa, że ​​będę chciał sąsiadów. Czytanie większego bloku z dysku generalnie nie kosztuje prawie nic, więc gdybym chciał jednego rekordu, wczytałem 1000 do pamięci podręcznej. Moje rekordy prawie na pewno były obok siebie, z SQL głowa dysku odbijałaby się wszędzie.
Loren Pechtel