• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed Odczyt pliku binarnego - błąd pamięci

Object Storage Arubacloud
0 głosów
676 wizyt
pytanie zadane 14 maja 2015 w C i C++ przez Emil Kob Bywalec (2,640 p.)
zamknięte 18 maja 2015 przez Emil Kob
do
                {

                        sp_licznik++;                                                   
                        stream_pos = stream_pos + sizeof(TEdit) * (sp_rok * sp_licznik);
                        plik.seekg(stream_pos);
                        plik.read((char*)&io_temp, sizeof(TEdit));
                } while
                        (!( io_temp->Text == Form1->Edit173->Text || plik.eof()));    

 

Pętla ta ma obsługiwać pewne wyszukiwanie w moim programie. Przeszukuję plik binarny w celu odpowiedniego ustawienia znacznika plikowego, tak aby móc zapisać wprowadzone dane w odpowiednim miejscu pliku (zamienić dane).

Zmienne:

sp_licznik - nie jest ważna przy rozwiązaniu tego problemu
stream_pos - nie jest ważna przy rozwiązaniu tego problemu

io_temp - jest to zmienna typu TEdit utworzona wcześniej ( czyli objekt klasy TEdit)

Form1->Edit173 - jest to pole tekstowe w głównym oknie programu (tych pól jest strasznie dużo, ale nie jest to ważne) [czyli obiekt kalsy TEdit]

Pola Edit są objektami klasy TEdit tak samo jak io_temp. Tylko pola Edit mają swoje odzwierciedlenie na formulażu programu w postaci pól, które będzie wypełniał użytkownik. Natomiast io_temp nie, jak można zasugerować się nazwą jest to tylko zmienna typu TEdit do tymczasowego przechowania danych.

Plik który przeszukuje jest zapisany blokami danych wielkości TEdit tak aby łatwo można było przestawić znacznik plikowy w odpowiednie miejsce.

PROBLEM

Wyszukiwanie odpowiedniego miejsca działa prawidłowo, odczytanie jednego bloku danych do zmiennej io_temp również. Problem pojawia się w momencie chęci skożystania z elementów tej zmiennej. Jest to klasa, więc można się odwoływać do jej elementów za pomocą operatorów -> np. io_temp->Text. Pojawia się komunikat AccessViolation, błąd dostępu do pamięci w momencie sprawdzania warunku pętli do while, czyli podczas sprawdzania warunku pętli .

komentarz zamknięcia: Zbyt zaawansowana wiedza jak na mój etap nauki. Poradziłem sobie w inny sposób.

3 odpowiedzi

0 głosów
odpowiedź 14 maja 2015 przez Wiciorny Ekspert (270,910 p.)

Instrukcja zawarta w pętli powtarzana jest, jeśli wyrażenie logiczne jest prawdziwe, tak definiujemy do ... while...

a ty masz Negacje logiczną z alternatywy ( którą w moim przypadku jest zawsze prawdziwa :), bo masz równoważność i  operator lub, więc z 2 możliwości obie musiały by być 0 żeby negacja była prawdą i pentla działała )

Nie wiem czy dobrze to zrozumiałem, to miałeś na celu ? 

komentarz 14 maja 2015 przez Emil Kob Bywalec (2,640 p.)
Nie o to chodzi. Warunek pętli działa prawidłowo tak jak chcę. Chodzi o to że gdy odczytać blok danych do io_temp i próbuje odwołać się do elementu io_temp->Text wywala błąd pamięci.
0 głosów
odpowiedź 14 maja 2015 przez Sebastian Fojcik Nałogowiec (43,040 p.)

Odczyt:

plik.read((char*)&io_temp, sizeof(TEdit));

Patrząc na deklarację funkcji istream::red (http://www.cplusplus.com/reference/istream/istream/read/).
Pierwszy argument, to skąd wczytać, a drugi ile znaków wczytać.

Najpierw wczytujesz tekst do obiektu klasy TEdit (io_temp), a potem odczytujesz wartość za pomocą: io_temp->Tekst.

W klasie TEdit może być wiele takich Tekst1, Tekst2, Tekst3. Skąd metoda read(), której podajesz tylko obiekt klasy TEdit wie, że odczyt ma załadować konkretnie do tej zmiennej - Tekst. (Być może zrobiłeś funkcję konwertującą, ale wolę się upewnić, bo nie wspominałeś nic o tym).

komentarz 14 maja 2015 przez Emil Kob Bywalec (2,640 p.)
edycja 14 maja 2015 przez Emil Kob
Metodę read() wykorzystuje do odczytywania plików binarnych i jako drugi argument podaje się wielkość w bitach (lub bajtach). Funkcja sizeof() zwraca wielkość obiektu klasy TEdit. Plik binarny z którego odczytuję zbudowany jest z obiektów o jednakowej wielkości, są to obiekty typu TEdit. Wiadomo, że w każdym z tych obiektów zapisana jest cała masa różnych danych, ale pole typu Text jest tylko jedno. Ustawiam znacznik plikowy w konkretne miejsce, odczytuję cały obiekt do io_temp. Problem z AccessViolation pojawia się przy próbie dostania do pola io_temp->Text.
komentarz 14 maja 2015 przez Sebastian Fojcik Nałogowiec (43,040 p.)
edycja 14 maja 2015 przez Sebastian Fojcik

Mhm... w skrócie, chodziło mi o to, czy poprawne wywołanie funkcji read() nie powinno wyglądać tak:

plik.read((char*)&io_temp.Tekst, sizeof(TEdit));

Teraz widać, że to co odczytamy, zapiszemy z zmiennej "Tekst".
I wtedy mamy prawo to odczytać: io_temp->Text

Możesz też zaraz przed wyskoczeniem błędu sprawdzić w debuggerze, czy wewnątrz: "Tekst" znajduje się to, co chciałeś odczytać. No i napisałeś, że "io_temp" to obiekt. A operator '->' jest przeznaczony dla "wskaźników" pokazujących na klasy.

"Wskaźnik" na klasę, to też "obiekt" tej klasy? Popraw mnie jeśli się mylę, ale tak raczej nie jest.

Definicja: "TEdit * io_temp" sam wskaźnik może być typu int (zależnie od kompilatora). Więc nie jest obiektem tej klasy, bo przechowuje tylko jej adres (jedną liczbę). Jeśli więc napisałeś, że io_temp jest obiektem klasy TEdit, to znaczy, że nie jest wskaźnikiem pokazującym na tę klasę. Czyli operator '->' nie zadziała.

komentarz 15 maja 2015 przez Emil Kob Bywalec (2,640 p.)
Nie można w ten sposób odczytywać tych danych, ponieważ są one całymi obiektami TEdit. Świadczy o tym podanie ich wielkości, którą zwraca sizeof(TEdit). tak io_temp to zmienna utworzona dynamicznie, czyli io_temp jest wskaźnikiem do tego obiektu. Stąd właśnie kombinacje z rzutowaniem danych (char *)&io_temp w metodzie read. I mam wrażenie że gdzieś tu tkwi problem. Do tych danych możemy dostać się tak *io_temp.Text lub io_temp->Text.
0 głosów
odpowiedź 16 maja 2015 przez Bartek85 Mądrala (7,440 p.)

Ciężko się pomaga, jeżeli nawet nie znamy typów zmiennych. Taki kod który wkleiłeś to jak wróżenie z fusów. Niemniej jednak widzę, tutaj małą nieścisłość. Pytanie jest takie: Jakiego typu jest zmienna io_temp?

jest to typ wskaźnika?

TEdit * io_temp;

czy typ wartościowy?

TEdit io_temp;

Z budowy kodu domyślam się jednak, że jest to typ wskaźnikowy, więc problem prawdopodobnie leży z linijce

plik.read((char*)&io_temp, sizeof(TEdit));

wg mnie powinno być:

plik.read((char*)io_temp, sizeof(TEdit));

 

Metodę read() wykorzystuje do odczytywania plików binarnych i jako drugi argument podaje się wielkość w bitach (lub bajtach). Funkcja sizeof() zwraca wielkość obiektu klasy TEdit.

Wielkość w bajtach.

 

komentarz 19 maja 2015 przez Emil Kob Bywalec (2,640 p.)
I wywala błąd pamięci :( . Może trzeba utworzyć tablicę i przechowywać w niej tylko wskaźniki do tych pól Edit. Czyli tablica będzie typu int (long int, double) ?
komentarz 19 maja 2015 przez Bartek85 Mądrala (7,440 p.)
Twók ostatni pomysł też nie zadziała, niestety. Ma tą samą wadę co poprzednie rozwiązania.

Jak dałbyś mi swój kod, to bym Ci napisał rozwiązanie. Ale teraz dam Ci wskazówkę jak to zrobić aby działało. Pole TEdit, jak rozumiem jest kontrolko GUI i jej jedynym zadaniem jest trzymanie jakiegoś ciągu znaków(stringa). Zgadza się?

Musisz wziąć pod uwagę, że każdy string może, a raczej na pewno będzie miał różną długość. Więc, zapisując plik musisz sobie zaplanować jego strukturę. Wiesz, że przed zapisem, znasz ile będzie wych elementów.

Aha, mała dygresja. Dane maja byc zapisane binarnie? Czy preferujesz jakiś plik txt, aby móc podglądać co się zapisało ?

Od tego jaka będzie powyższa odpowiedz, przykładowe rozwiązania są dwa:

- zapis binarny: JA bym zrobił to tak... Wiem jaka jest liczba obiektów, więc pierwsze pole do pliku zapisuje ilośc elementów, następnie jeden za drugim zapisuje: napierw rozmiar w bajtach ciągu który ma być zapisany, a później ten ciąg znaków, później następne znów rozmiasr nastepnego ciągu nakowego i sam ciag znakowy(string), itd aż do końca.

Znając strukturę pliku odczytujesz sie w odwrotny sposób.

- druga sprawa jest ławiejsza i mniej profesjonalna.(plik txt)

zapisujesz ciągi znaków jako kolejne wiersze w pliku.

Odczytujesz analogicznie.

Mam nadzieję, że pomogłem. Jeżeli nadal czegoś nie rozumiesz, przygotuję Ci malutki programik który to ilustruje.
komentarz 19 maja 2015 przez Emil Kob Bywalec (2,640 p.)
Tak, Edit jest kontrolką GUI. Wiem o co chodzi, Twoje podejście jest jak najbardziej słuszne. Wydawało mi się, że plik powinien być binarny ponieważ tak będzie łatwiej, ale jednak wychodzi na to, że tak nie jest :). Plusem jest to, że Stringi zapisane w polu Edit w większości są jednakowej wielkości, ale nie wszystkie. Rozwiązaniem może być plik tekstowy i chyba tak zrobie.

Mój program ma służyć do ewidencji godzin pracy oraz obliczania pensji. Dane zapisane w pliku będę musiał odszukiwać, modyfikować pozycje konkretnych godzin rozpoczecia, zakończenia pracy itd... w trakcie pracy programu (prełączania przez urzytkownika lat lub miesięcy).

Program robię w Borland Builder 6, załączam i plik projektu, możesz sobie zerknąć.

https://drive.google.com/folderview?id=0B5fAb-41dYhlU0hjMDl1VnIxVjA&usp=sharing
komentarz 20 maja 2015 przez Bartek85 Mądrala (7,440 p.)

Poprawiłem tą funkcję zapisu. Poniżej wklejam kod. Jak będziesz miał jakieś pytania to pytaj. Mimo wszystko mam nadzieję, że jest czytelnie.


//Zapis

void Zapis ()
{
        fstream plik;
        plik.open("lim3.bok", ios::binary | ios::out);

        if (!plik.is_open())
        {
              ShowMessage("Error during opening file.");
        }
        else
        {
          int numberOfElements = 31 * 6;
          plik.write((char*)&numberOfElements, 4);

          for (int i = 0 ; i < 31 ; i++)
            for (int j = 0 ; j < 6 ; j++)
             {
               // I made assumption, that every single element of array
               // tablica_wsk_Edit is not NULL, so I don't check it.
               int sizeOfElement = tablica_wsk_Edit[i][j]->Text.Length();
               char* ptrToData = new char[sizeOfElement + 1];
               memset(ptrToData, 0, sizeOfElement + 1);
               memcpy(ptrToData, tablica_wsk_Edit[i][j]->Text.c_str(), sizeOfElement);

               plik.write((char*)&sizeOfElement, sizeof(int));
               plik.write(ptrToData, sizeof(char) * sizeOfElement);

               delete[] ptrToData;
             }

           plik.close();
        }
}

//Odczyt

void Wczytaj ()
{
        fstream plik;
        plik.open("lim3.bok", ios::binary | ios::in);

        if (!plik.is_open())
        {
              ShowMessage("Error during opening file.");
        }
        else
        {
          int numberOfElements;
          plik.read((char*)&numberOfElements, sizeof(int));

          for (int i = 0 ; i < 31 ; i++)
            for (int j = 0 ; j < 6 ; j++)
             {
               int sizeOfElement;
               plik.read((char*)&sizeOfElement, sizeof(int));
               char *tmp = new char[sizeOfElement + 1];
               memset(tmp, 0, sizeOfElement + 1);
               plik.read(tmp, sizeof(char) * sizeOfElement);

               // I made assumption, that every single element of array
               // tablica_wsk_Edit is not NULL, so I don't check it.
               tablica_wsk_Edit[i][j]->Text = AnsiString(tmp);

               delete[] tmp;
             }

           plik.close();
        }
}

BTW: Sama architektura pozostawia wiele do życzenia... może nawet jej brak. Mam nadzieję, że to dopiero Twoje początki.


Nie myślałeś aby przesiąść się na Visual Studio ? na C++/C# ? Borland to juz historia.

komentarz 21 maja 2015 przez Emil Kob Bywalec (2,640 p.)
Masz rację co do architektóry, jest benadziejna, ponieważ jeszcze nie do końca ogarniam co gdzie powinienem umieszczać. To mój pierwszy program z interfejsem wizualnym. Oczywiście, że kolejny program będę pisał w VS, będzie to interfejs do sterowania ramieniem robota, komunikacja przez com, lub innym sposobem.

Dziękuję za funkcję, muszę je rozumieć zanim je zastosuje, ale chyba zrobię tak jak pisaliśmy wcześniej, że będę zapisywał dane do pliku tekstowego. Będzie to mniej profesjnalne, ale zrobione przeze mnie :)

Podobne pytania

0 głosów
0 odpowiedzi 133 wizyt
pytanie zadane 4 lutego 2017 w C i C++ przez dzialkowiec Nowicjusz (220 p.)
+1 głos
0 odpowiedzi 64 wizyt
–1 głos
0 odpowiedzi 475 wizyt
pytanie zadane 23 września 2017 w C i C++ przez 10kw10 Pasjonat (22,880 p.)

92,632 zapytań

141,498 odpowiedzi

319,872 komentarzy

62,011 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...