Nie mogę wczytać całego pliku do pamięci


(Lena(R)) #1

Witam

Jestem młodym programistą, w sumie dopiero od roku, tak na poważnie, zacząłem przygodę z programowaniem. A w zwiazku z tym, jeżeli moje pytanie wyda się głupie, to proszę o wybaczenie.

Próbóje wczytać dane z pliku tekstowego do pamięci komputera. Zapisuje te dane do dynamicznej tablicy klas.

Podczas wczytywania dostaje info że program przestaje działać, i próbuje wsyłać jakiś raport do microsoftu o błędzie, w sumie jak to się mówi, program mi się wysypuje.

Natomiast kiedy ustawie tak, aby wczytał tylko część pliku, to działa bez problemu. Czyżby plik tekstowy który ma rozmiar dokładnie 4,61KB może nie zmieścić się w pamięci komputera? Czy problem leży raczej w czymś innym?

Programuje w C++, używam kompilatora Code::Blocks 8.02 i obecnie pracuje na Windows 7, ale pod Vista miałem dokładnie ten sam problem.


(Ryan) #2

Plik 4kb zmieści Ci się bez problemu. Masz błąd w kodzie - najprawdopodobniej przepełnienie bufora przy wczytywaniu do pamięci. Przy mniejszej ilości danych pewnie masz szczęście i nie nadpisujesz niczego (w danej chwili) istotnego.


([alex]) #3

W pamięci może się pomieścić prawie tyle ile masz fizycznie pamięci (ciągłego obszaru).

Kompilator tu niema nic do rzeczy, programista zawinił.

Code::Blocks - nie jest kompilatorem.


(Lena(R)) #4

To jak mogę uprzątnąć buffor, żeby sprawdzić czy przypadkiem go nie przepełniam? Korzystam z biblioteki


([alex]) #5

Problem masz nie ze sprzątaniem bufora lecz z jego przydzieleniem.

char buf[4096];FILE *in=fopen("plik.txt","r"); int len=fread(buf,1,4096,in);fclose(in);len4096) buf[len]=0;cout"Plik jest zdecydowanie za długi"endl; [/code]Co robisz nie tak?

(Ryan) #6

Granicą jest największy ciągły obszar logicznej przestrzeni adresowej procesu (zakładając, że jest dostępny swap file).


(Lena(R)) #7

Gdybym ja to wiedział co robie nie tak.

Wczytuje funkcja fscanf do tablicy (char linia[150]; ) linijke po linicje z pliku (w kodzie ponizej "anno" to nazwa klasy, "realokuj" nie trudno sie chyba domyslec funkcja ktora realokuje pamiec). Po odczytaniu kazdej z linii, przepisuje odpowiednimi funkcjami klasy caly string z tablicy "linia" do skladnikow prywatnych klasy, po czym zeruje ta tablice.

anno *tab = NULL;

    FILE *odczyt;

    int rozmiar = 0;


    if((odczyt = fopen("plik.txt", "rt")) != NULL)

    {

    cout << "Wczytywanie...";

        for(int i = 0; i < 7; i++)

        {

            fscanf(odczyt, "%s\n", linia);

            if(i == 0)

            {

                tab = new anno[++rozmiar];

                i++; //i++, poniewaz odrazu powinien sie wykonac nastepny warunek, bo to ta sama linia

            }

            if(i > 0)

                tab = realokuj(tab);


        //zerowanie zmiennej linia-----------------

        for(int j = 0; j < 150; j++)

            linia[j] = NULL;



        //----------------------

        }

    fclose(odczyt);


    cout << "OK";

    }

Jezeli kod wyglada jak powyżej nie ma problemów, wszystko działa. Jednak jeżeli przy pętli for zamiast 7 wstawie większą liczbę to program się wysypuje.

for(int i = 0; i < 7; i++)

Tam gdzie jest ta 7 to powinna być ostatnia linia w pliku (a tych lini jest dokladnie 123).


([alex]) #8

(Ryan) #9

Apeluję o to samo. I - ponownie - o to, żebyś mnie nie traktował protekcjonalnie.

Ułożenie danych w pamięci fizycznej (jak i to czy się mieści) jest w każdym nowoczesnym systemie operacyjnym maskowane przez manager pamięci. Póki może rosnąć plik wymiany i nie skończyła się logiczna przestrzeń adresowa (jedyna, którą widzi aplikacja) - możesz alokować pamięć. Nic zatem nie stoi na przeszkodzie byś np. na starcie aplikacji zaalokował 1.5GB pamięci mimo iż masz tylko 1GB pamięci fizycznej (ale już np. 4GB na procesorze 32-bitowym nie zaalokujesz, bo część przestrzeni adresowej jest zawsze przesłonięta a zaadresować możesz tylko 2^32 bajtów).


([alex]) #10

Owszem możesz przydzielić 1.5GB pamięci mimo iż masz tylko 1GB o ile nie jest to pamięć ciągła. czyli możesz przydzielić np w kawałkach 0.9GB + 0.6GB ale jednego kawałku długością nawet dokładnie 1GB już nie przydzielisz. Czyli dokładnie jak napisałem - ciągły obszar może być najwyżej prawie tyle ile masz pamięci fizycznej.

Przesadzasz, nie ma w tym nawet odrobinę protekcjonalizmu. Jedynie zakładam że wiesz dokładnie jak to się dzieje z pamięcią tylko nie do końca zrozumiałeś moją wypowiedź, bo niezbyt precyzyjnie się wyraziłem.


(Ryan) #11

Nie. Aplikacja nie widzi pamięci fizycznej. Ważna jest przestrzeń adresów logicznych. Nie przeczę, że jeśli w przestrzeni adresów logicznych jest wyrwa dokładnie w środku, to nie zaalokujesz sobie 2GB (co przeważnie nie jest problemem - w Windows aplikacje są ładowane w górych 10% przestrzeni adresowej). Nadal jednak ograniczeniem jest przestrzeń logiczna a nie fizyczna - memory manager dzieli przestrzeń logiczną na kilkukilobajtowe strony i umieszcza je w pamięci fizycznej tam gdzie jest miejsce (a jak nie ma, umieszcza na dysku).

To zamiast pisać "przemyśl to", wyjaśnij o co Ci chodziło. Ja staram się tak właśnie robić - w przeciwnym wypadku pojawiają się zbędne "cykle" wymiany postów na forum.


([alex]) #12

Proponuje zrobić małą aplikacje testową. :stuck_out_tongue:

To zamiast pisać "przemyśl to", wyjaśnij o co Ci chodziło. Ja staram się tak właśnie robić - w przeciwnym wypadku pojawiają się zbędne "cykle" wymiany postów na forum.


(Ryan) #13

Proponuję zapytać Landy'ego albo Apurvę odpowiadających za memory manager w Windows. :stuck_out_tongue:


([alex]) #14

Proponuje zrobić małą aplikacje testową pod linuksa. :stuck_out_tongue:


(Lena(R)) #15

Ale kazda linijka w pliku tekstowym ma mniej niz 150 znakow, wiec podanie char linia[150] i tak jest tablica wieksza niz potrzeba (tak na wszelki wypadek). Wiec spokojnie, nie zrobilem tego na ślepo.

Rozumiem ze linia[j] = NULL; moze sugerowac ze jest to tablica wskaznikow, ale raczej tylko sugeruje. Ale w takim razie, jak powinienem "wyzerowac" tablice znaków, inaczej niż wstawiając wszędzie NULL? Poza tym, nigdy nie używałem zwykłego C, więc niestety nie wiem, gdzie jest ta mieszanka.

A tak poza tym, czy gdzieś widziecie błąd w tym kodzie co napisałem?


([alex]) #16

linia[j]=0; lub linia[j]='\0';

W C++ skoro już korzystasz z cin/cout to czemu nie korzystasz z fstream ?

Co do pliku... Czy użytkownik nie może zmienić go w notepad'zie przed uruchomieniem twojego programu?

Odpowiednio dobrany ciąg znaków może doprowadzić do wykonania kawałku tego ciągu jako instrukcje procesora.


(Lena(R)) #17

Dobra panowie. Doszukałem się błędu. Jak wspomniałem na początku, jestem początkującym programistą więc mam prawo, mam nadzieje, do takich błędów. Krótko powiem co skopałem (chętni mogą przeczytać :stuck_out_tongue: ) i dla mnie koniec tematu w tej sprawie.

Problem był z alokacją pamięci, a właściwie jej realokacją. Nigdy do tej pory, nie miałem okazji, ani potrzeby używać tablic dynamicznych w klasach. Ten program to był wsumie eksperyment (bo na początku miałem w klasie używać zwykłych tablic, ale pomyślałem, że trzeba pójść o krok dalej i zrobimy je dynamiczne, trzeba w końcu iść do przodu, jeśli chce się być dobrym programistą...podobno). Więc ja poprostu, z przyzwyczajenia, kiedy robiłem realokację tablicy obiektów klasy, to składniki tej klasy przepisywałem na zasadzie

strcpy(klasa.tablica, klasa2.tablica);

W finale tago nieszczęsnego kopiowania (jeśli dobrze kojarze, to się nazywa kopiowanie "składnik, po składniku"), przez pierwsze 7 linijek odczytywanie z pliku robiłem realokacje tablic (w klasie) z krótszy do dłuższych. I dlatego wszystko było ok. Następna 8 była krótsza od poprzedniej i program się wywalał, bo nagle przy 8 realokacji chciałem przepisac, wlasnie funkcją strcpy(), z tablicy dłuższej do krótszej.

No i to był ów błąd mojego programu. Gdybym wcześniej bardziej się skupił na tym co jest w pliku do odczytania, może szybciej wyłapałbym ten błąd. Ale ja się uparłem, że to coś z moim kodem jest nie tak. Ostatecznie wszystko się udało.

Człowiek uczy się na błędach, a programista też człowiek i może takie błędy popełniać, żeby potem ich nie powtarzać :stuck_out_tongue: Szczególnie młody programista :smiley:

Dzięki chłopaki za próbę pomocy, ale ostatecznie, jak to zasugerował na początku [alex]: "zawinił programista" i tak też było.

I to tyle byłoby, z mojej strony, w tym temacie. Jeszcze raz dzięki.