zapis binarny do pliku

0

Drodzy koledzy z 4p, nastał wielki kryzys gdyz od kilku godzin sie mecze i debuguje i nie moge sobie poradzic z zapisem i odczytem binarnym, pomimo ze wszystko dobrze szlo do dzisiaj, pomozcie prosze.
wiec mam strukture ktora sobie przechowuje date w 3 intach : int dzien, int miesiac, int rok.
wiec mam takie 2 obiekty w klasie ktora porownujac dokonuje zapisu, akutalizacji albo dodania nowego rekordu.
i tak jak sobie zapisuje skladniki mojej klasy Obiekt_glowny binarnie, to wszystko pieknie, ale jak chce zapisac ze skladnika tej klasy pierwszyCzas.dzien etc. to jak odczytam to juz nie jest pieknie.
Co robie źle?
Zapisuje tak:

ofstream Wyjscie(NazwaWyniku.c_str(), ios_base::binary);
            char* temp;
            int itemp(0);
            temp = new char[sizeof(int)];
            itemp = PierwszyCzas.Dzien; temp = (char*)(itemp);    
              Wyjscie.write(temp, sizeof(int)); cout << ((int)(*temp));
            itemp = PierwszyCzas.Miesiac; temp = (char*)(itemp);   
              Wyjscie.write(temp, sizeof(int)); cout << ((int)(*temp));
            itemp = PierwszyCzas.Rok; temp = (char*)(itemp);      
               Wyjscie.write(temp, sizeof(int)); cout << ((int)(*temp));
//tu wyzej dodalem itempa bo myslalem ze pomoze... nizej bez itempa
            temp = (char*)(&OstatniCzas.Dzien);     Wyjscie.write(temp, sizeof(int)); cout << ((int)(*temp));
            temp = (char*)(&OstatniCzas.Miesiac);   Wyjscie.write(temp, sizeof(int)); cout << ((int)(*temp));
            temp = (char*)(&OstatniCzas.Rok);       Wyjscie.write(temp, sizeof(int)); cout << ((int)(*temp));

Odczytuje tak:

         char* temp;
        ifstream Wejscie(NazwaWyniku.c_str(), ios_base::binary);
        if(Wejscie.good()){
            temp = new char[sizeof(int)];
                if(PierwszyCzas.Dzien == 0){

                    Wejscie.read(temp, sizeof(int)); PierwszyCzas.Dzien = (int)(*temp);
                    Wejscie.read(temp, sizeof(int)); PierwszyCzas.Miesiac = (int)(*temp);
                    Wejscie.read(temp, sizeof(int)); PierwszyCzas.Rok = (int)(*temp);
....reszta odczytu/

przykladowo dla dzisiejszej pechowej daty
1962012 - odczyt z tempa to : 196-36
dodam ze reszte skladnikow klasy dobrze mi czyta tylko w tej wewnetrznej strukturze z czasem cos sie chrzani.
pozdrawiam
ChS

0

Wychodzi na to ze 2012 jest zle konwertowane, ale nie potrafie tego naprawic.
Ciekawe czemu i ile jest takich kombinacji jak 2012 (int)->(char*) = (char*)->(int) -36
jak wiec to zapisac do pliku binarnego?
1h mniej, coraz mniej pomyslow w glowie mam
ChS

0

if(PierwszyCzas.Rok == -36) PierwszyCzas.Rok = 2012;
Zrobilem na razie zamiane tego bledu na poprawna wartosc ale jak bedzie kolejny rok to juz nikt nie odpali programu wiec to nie jest zbyt rozsadne rozwiazanie.
Jak ktos sie spotkal z takim problemem w tym roku badz innym z inna pechowa konwersja, prosze o zostawienie jakiejs podpowiedzi.

peace
ChS

0

Powodem jest to: (int)(*temp) - najpierw wyłuskujesz z wskaźnika char, czyli de facto pierwszy bajt wczytanego inta, a potem tą wartość konwertujesz na int. Powinno być tak ... = *((int*)temp);.

Tyle, że skoro piszesz w C++ to wypadałoby korzystać z C++, a nie C ;) - ... = *(reinterpret_cast<int*>(temp));
A tak na prawdę lepiej unikać, a przynajmniej bardzo uważać z tymi konwersjami ze względu na optymalizacje, które mogą to rozsypać(strict aliasing, które gcc ma domyślnie włączone) - jeżeli dobrze pamiętam, to ominąć ten problem pozwala użycie memcpy.

0

A czemu nie zrobić tego po ludzku?:

            int temp;
            char *ptemp=(char*)&temp;
            temp = PierwszyCzas.Dzien;   Wyjscie.write(ptemp, sizeof(int));
            temp = PierwszyCzas.Miesiac; Wyjscie.write(ptemp, sizeof(int));
            temp = PierwszyCzas.Rok;      Wyjscie.write(ptemp, sizeof(int));
 
            int temp;
            char *ptemp=(char*)&temp;
            Wejscie.read(ptemp, sizeof(int)); PierwszyCzas.Dzien = temp;
            Wejscie.read(ptemp, sizeof(int)); PierwszyCzas.Miesiac = temp;
            Wejscie.read(ptemp, sizeof(int)); PierwszyCzas.Rok = temp;
0

@_13th_Dragon
Dziala, znowu widze po Twoim kodzie, ze przydatne sa te ludzkie metody
Dzieki ponownie

@byku_guzio
Twoj sposob wywala mi blad pamieci, ale uzywam MinGW, moze dlatego sie tak dzieje tak jak pisales wyzej

No to moge przerobic na nowy, efektywniejszy kod
dzieki chlopaki, pozdro

ChS

0

Można też tak:

Wyjscie.write((const char*)&PierwszyCzas, sizeof(PierwszyCzas));
Wejscie.read((char*)&PierwszyCzas, sizeof(PierwszyCzas));

na to samo wyjdzie.

@00001010 jeśli interesuje cię wyłącznie system Windows to problemu nie ma, ale miej na uwadze to, że w innych systemach int może mieć inną długość niż 32 bity oraz kolejność bajtów w takim int może być odwrócona (tzw. endianess).

0

Nie mam nic przeciwko że w newbie podnosic poziom

@adf88 słyszałem o big endianach i little endianach ale
nigdy nie zastanawiałem się jak się sprawdza tą kolejność bitów a
wiadomo że oprócz Windowsa i Linuksa są setki innych OS'ow.
Są jakies konkretne metody czy trzeba sprawdzać Right/Left shiftem
odleglosc ostatniego bitu -
binarymax = (sizeof(int)*8)
operacja (sizeof(int)*8) - 1 (dec) daje w wyniku same 1 i jedno 0
0 jest z lewej albo prawej

Taki sposob w pierwszej chwili wpadl do glowy.

0
int x=1;
if(*((char*)&x)) //jedynka na początku
else //jedynka na końcu
0

Ok ale nie moge nadpisac tych danych binarnych podczas akutalizacji (68bajtow) z czego 10 bajtow to informacje o strukturze pliku,
czyli sa 2 przypadki : 1 rekord albo wiecej rekordow,
czyli mam skopiowac poczatek i te rekordy co mam oprocz ostatniego,
wymazac plik z wrzucic zrzut z pamieci i dodac na konciu ostatni rekord?
to taka pseudo aktualizacja wyjdzie ktora za rok by zajmowala minute zamiast sekundy.

0

Masz dwa podejścia:

  • Przymujesz że w pliku informacja jest zapisana w jakimś konkretnym Endian'ie więc przed zapisem i po odczycie każdej liczby całkowitej trzeba sprawdzić Endian'a systemu i jeżeli się różni od formatu pliku to trzeba skonwertować liczbę.
  • Przy zapisie w informacji nagłówkowej wpisujesz Endian'a systemu (np: x=1; write(x); ), zaś przy odczycie porównujesz to co zapisano w nagłówku z Endian'em systemu (np: read(x); if(x!=1) ... ) jak się nie zgadza - konwertujesz.
    Do konwersji są funkcje htons(), htonl(), ntohs(), ntohl()
0

uzywam fseekow i otwieram w trybie dodaj

uzywam Twojej konstrukcji aliasingu wskaznikiem char
            int itemp;
            float ftemp;
            char* temp = (char*)(&itemp);
ofstream Wyjscie(NazwaWyniku.c_str(),  ios::binary | ios_base::app);
//omijam pierwsze 3 inty ktore sie nie zmieniaja przy aktualizacji
//                temp = (char*)(&PierwszyCzas.Dzien);     Wyjscie.write(temp, sizeof(int));
//                temp = (char*)(&PierwszyCzas.Miesiac);;  Wyjscie.write(temp, sizeof(int));
//                temp = (char*)(&PierwszyCzas.Rok);       Wyjscie.write(temp, sizeof(int));
            Wyjscie.seekp(3*sizeof(int), ios::beg);
            itemp = AllGoodPt;              Wyjscie.write(temp, sizeof(int));
            itemp = AllPtEver;               Wyjscie.write(temp, sizeof(int));
            itemp = IloscRekordow;        Wyjscie.write(temp, sizeof(int));
            //pierwszy rekord = 6*sizeof(int)
//zakladam ze to zadziala ale nie dziala           

            if(IloscRekordow == 1) Wyjscie.seekp(6*sizeof(int), ios::beg);
            else Wyjscie.seekp((IloscRekordow-1)*(6*sizeof(int)+(10*sizeof(int) + sizeof(float))) , ios::beg);
           
 //kazdy kolejny rekord = 10*sizeof(int) + sizeof(float)

metoda seekp nie dziala... moze zle jej uzywam? teraz to juz zdebialem

1
_13th_Dragon napisał(a):

Masz dwa podejścia:

  • Przymujesz że w pliku informacja jest zapisana w jakimś konkretnym Endian'ie więc przed zapisem i po odczycie każdej liczby całkowitej trzeba sprawdzić Endian'a systemu i jeżeli się różni od formatu pliku to trzeba skonwertować liczbę.
  • Przy zapisie w informacji nagłówkowej wpisujesz Endian'a systemu (np: x=1; write(x); ), zaś przy odczycie porównujesz to co zapisano w nagłówku z Endian'em systemu (np: read(x); if(x!=1) ... ) jak się nie zgadza - konwertujesz.
    Do konwersji są funkcje htons(), htonl(), ntohs(), ntohl()

bez przesady o czym Wy tu dyskutujecie
przecież endianness jest znany w momencie kompilacji, przy jego zmianie trzeba przekompilować program więc nie trzeba w kodzie programu robić żadnych testów

http://en.wikipedia.org/wiki/Endianness#Endianness_and_operating_systems_on_architectures
Zarówno x86, x86-64, jak i ARM są w little endian - więc wszystkie najpopularniejsze systemy - windowsy (w tym RT), linuksy na PC, Android na komórkach i tabletach, iOS, macosx masz z głowy
więc na co chcesz pisać ten swój mega program że się przejmujesz kolejnością bajtów? :|

0

Bardzo dobra uwaga, skoro ma sie rozpoczac era bardziej inteligentnych programow to sprawdzenie endiana nawet w 3 kategoriach, low, big, uknown, pewne problemy by rozwiazalo ,
ale dalej mi nie chce nadpisac binarnie tego pliku,
wiec albo w zlym trybie otwieram albo zle przesuwam po pliku seekp()

0
00001010 napisał(a):

if(IloscRekordow == 1) Wyjscie.seekp(6sizeof(int), ios::beg);
else Wyjscie.seekp((IloscRekordow-1)
(6sizeof(int)+(10sizeof(int) + sizeof(float))) , ios::beg);

//kazdy kolejny rekord = 10*sizeof(int) + sizeof(float)

> metoda seekp nie dziala... moze zle jej uzywam? teraz to juz zdebialem

Zakładając że próbujesz odczytać ostatni rekord to:
```cpp
Wyjscie.seekp(6*sizeof(int)+(IloscRekordow-1)*(10*sizeof(int) + sizeof(float)) , ios::beg); // Niezależnie od IloscRekordow

lub:Wyjscie.seekp(-(10*sizeof(int) + sizeof(float)),ios::end);

0

ok problemem bylo ze mialem zla flage wlaczona, zamiast ios_base::ate mialem ios_base::app .
juz dzialalo ale idac dalej, pomyslalem ze warto pogrupowac to
w wewnetrzne struktury i czytac cala strukture binarnie,
zeby zapewnic odczyt kazdego rekordu do pojemnika,
dotychczas odczytywalem tylko ostatni rekord

zabawa sie zaczela kiedy wczytalem cala strukture, tj.
pierwszy rekord zawiera
strukture czas; - ta z kolei zawiera 6 intow, string i bufor char
3 inty

kolejny rekord zawiera
10 intow
1 float

i ladnie mi sie sypalo to wszystko bo dodawalo do czasu dane ktore do pliku nie byly zapisane, i zauwazylem jak to ciekawie zawile

w tym wypadku musialem wyciagnac obydwie struktury i dodam jeszcze 3 czas najwyzej bede mial
pierwszy czas, aktualnie czytany i czas ostatniego rekordu.

ale wydaje mi sie ze w ciagu dnia dowiedzialem sie ciekawych rzeczy, tak wiec znowu podziekuje za uczaca dyskusje.

peace
ChS

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