Elektroda.pl
Elektroda.pl
X
Elektroda.pl
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[c++] eof - podwójne wczytywanie ostatniego wiersza

27 Mar 2011 23:24 4517 11
  • Poziom 12  
    Witam,
    w ramach nauki c++ dostałem takie zadanie: odczytać z pliku binarnego 4 kolumny (pierwsza- short int, 3 pozostałe double), a następnie obliczyć dla nich średnią, odchylenie standardowe itd.

    Program generalnie wykonuje to co powinien, ale jest jedno małe "ale": ostatni wiersz sczytywany jest dwukrotnie, co "nieco" zmienia wyniki. W celu określenia końca pliku używam "eof", tzn schematycznie część odpowiadająca za odczyt z pliku wygląda tak:

    Code:
    while(!strumien.eof())
    
    {
       operacje na danych z pliku;
       licznik++;
    }


    Pytanie właściwe: co zrobić, żeby eof zadziałał jeden "przebieg" wcześniej?
    Jak na razie w necie z wartościowszych rzeczy znalazłem coś takiego:link , ale jakoś nie mogę dostosować tego do mojego zadanka.

    Próbowałem znajdować koniec pliku (a właściwie strumienia) innymi funkcjami, ale rezultaty nie były najlepsze. Próbowałem też zastosować bufor, ale w pewnym momencie strasznie zagmatwałem kod i stwierdziłem, że najlepiej by było dowiedzieć się jak powinien być użyty (właściwie) eof.

    Z góry dzięki
    Tomek
  • Poziom 32  
    Takie używanie eof w C++ to chyba jakaś zaszłość z pascala. W C/C++ po prostu sprawdza się wynik czytania. iostreams nic tutaj nie zmieniają, jedynie fakt wystąpienia błedu zapamiętywany jest w strumieniu i można go sprawdzić m.in. metodą eof(). Inaczej mówiąc - ta metoda zwraca true dopiero po tym jak już któryś odczyt wyjdzie poza plik (czyli się nie uda w całości), a nie gdy pozycja czytania znajdzie się na końcu. Trzeba to sprawdzać po próbie wczytania a nie przed.
  • Poziom 12  
    Czyli bezpośrednio nie można sprawdzić gdzie jest koniec? Pozostaje zabawa z buforem?

    Mógłbyś mi jeszcze powiedzieć jakimi funkcjami sprawdza się wynik czytania? scanf?
  • Poziom 18  
    Myślę, że kolega krru niezbyt dokładnie to wytłumaczył. Nie ma nic złego w używaniu funkcji eof() strumienia, o ile się wie, gdzie i do czego ją zastosować. Natomiast oryginalny problem polega na tym, że w liniach pliku z danymi jest jeszcze coś więcej, oprócz czterech cyfr: znak końca linii. Ten znak pozostaje w strumieniu w momencie odczytania ostatniej linii z liczbami, dlatego jeszcze raz uruchomi się "while". Nie powiedzie się dopiero następna operacja odczytu, ale w zmiennych pozostaną ostatnie wartości.

    Są dwa sposoby wyjścia z tej sytuacji. Możesz pozostawić swój kod i dodać sprawdzenie stanu strumienia po odczytaniu pierwszej liczby:
    Code:
      while( strumien ){
    
        strumien >> a;
        if( !strumien ) break;
        strumien >> b >> c >> d;
       // Dalsze przetwarzanie wczytanych liczb
    }

    Zauważ, że poprawiłem nieco sprawdzanie stanu strumienia - nie trzeba jawnie używać funkcji eof(), załatwiają to przeładowane operatory.

    Drugi sposób, bardziej typowy, polega na przeniesieniu odczytu do warunku pętli while() - myślę, że to postulował kolega krru:
    Code:
      while( strumien >> a >> b >> c >> d ){
    
      //   Dalsze przetwarzanie...
      }

    Od razu widać, że drugi kod jest w tym przypadku prostszy.

    HTH,
    Dariusz
  • Poziom 12  
    Chyba niechcący coś przemilczałem. Ani jeden ani drugi sposób nie działa tak jak powinien. Oto cały kod:

    Kod: cpp
    Zaloguj się, aby zobaczyć kod


    Może nie jest super elegancki, ale podstawowe założenie spełnia (zresztą na dobrą sprawę jeszcze nie skończyłem pisać programu- na razie stanąłem na tym, że chciałbym rozwiązać problem podwójnego sczytywania). Dwukrotnie otwieram strumień, bo jakoś nie mogłem wpaść na to jak inaczej pobrać wartości z pierwszego wiersza i dalej w prosty sposób kontynuować operacje.

    EDIT
    Myślałem, że domyślnie jest tu coś takiego, że przydługawy kod można przewijać.
    Mogę to jakoś ustawić?

    Proszę pamiętać o używaniu znaczników syntax. Opcja Listing kodu. - arnoldziq
  • Poziom 18  
    Och, nie zamierzam za Ciebie tego kodu dopracować. Zwrócę jednak Twoją uwagę na błędy:
    - niepotrzebne otwieranie w trybie binarnym
    - niepotrzebne reinterpret_cast-y
    - mieszanie operacji formatowanych (czyli wczytywanie operatorem >>) i nieformatowanych (read())
    - niepotrzebne podwójne otwieranie pliku - po co?
    - niepotrzebne używanie printf().

    Ten program powinien mieć ok. 20 linii!

    HTH,
    Dariusz
  • Poziom 32  
    Dlaczego uważasz, że otwieranie w trybie binarnym jest niepotrzebne?

    Natomiast do kol. analfabeta mam pytanie: jak w końcu zapisane są dane w pliku? To mieszanie >> i read w jedym strumieniu wyglada bardzo podejrzanie.
  • Poziom 12  
    Ok, poprawie go wieczorem, ale mógłbyś spojrzeć dlaczego program praktycznie w ogóle się nie uruchamia, tzn. pętla chyba w ogóle się nie wykonuje?

    Co do otwierania w trybie binarnym- wg skryptu mam odczytać plik binarny (co jest podkreślone, więc uznałem, że chodzi o użycie tego trybu). A jeżeli chodzi o reinterpret_cast-y - zrozumiałem to w ten sposób, że są tu niezbędne.
  • Poziom 18  
    No tak, wątek pliku binarnego możemy pociągnąć, bo jest ciekawy. Co, Waszym zdaniem, Koledzy, takie otwarcie w trybie binarnym powoduje?

    Dariusz
  • Poziom 12  
    krru napisał:
    Natomiast do kol. analfabeta mam pytanie: jak w końcu zapisane są dane w pliku? To mieszanie >> i read w jedym strumieniu wyglada bardzo podejrzanie.


    Zdaje się, że jedyne">>" znajdują się na początku pętli. Umieściłem je tam, bo chciałem sprawdzić podpowiedź Dariusza Bismor (post z godziny 8.23).



    Co powoduje otwarcie w pliku binarnym wydaje mi się, że wiem, ale zapewne za chwilę okaże się, że się mylę :D.
  • Poziom 32  
    Dariusz Bismor napisał:
    No tak, wątek pliku binarnego możemy pociągnąć, bo jest ciekawy. Co, Waszym zdaniem, Koledzy, takie otwarcie w trybie binarnym powoduje?


    Otwarcie w trybie binarnym powoduje, że nie dokonywana jest konwersja znaków końca linii, każdy bajt odczytywany jest dokładnie tak, jak jest zapisany w pliku. Co dzieje się w przeciwnym wypadku zależy od systemu i np. pod DOS/Windows może być problem z odczytaniem bajtów "\r\n" - które przecież mogą wystąpić w binarnym pliku.

    Cały czas pozostaje podstawowy problem - jak zapisane są dane w pliku?
  • Poziom 18  
    Faktycznie, nie znając formatu pliku wejściowego, trudno więcej napisać. Poza tym gratuluję odrobienia zadania! - tryb binarny został rozszyfrowany. Wiele osób sądzi, że jeśli się nie otworzy w trybie binarnym, nie można wykonywać read()/write(), co jest przekonaniem błędnym. Poza tym w przypadku systemów POSIX tryb binarny nic nie robi (jest ignorowany), a autor oryginalnie nie pisał, czy pracuje na Win, czy MacOS, czy Linux.

    Moim zdaniem możemy się spodziewać dwóch przypadków:
    1. Plik jest zapisywany i odczytywany tylko operacjami read() i write() - wtedy tryb binarny nie jest potrzebny, bo te niskopoziomowe operacje nie traktują w żaden specjalny sposób znaku nowej linii. Zresztą, taka jest idea pliku nie przeznaczonego do czytania przez człowieka - nie ma potrzeby stosować znaków nowej linii.
    2. Plik jest jednak tekstowy - wtedy tryb binarny w ogóle nie powinien być użyty.

    Co do samej idei zapisu danych typu double przez te reinterpret_cast-y, to jest to wysoce nieprzenośne i wrażliwe na błędy - tak w ogóle się robić nie powinno!

    HTH,
    Dariusz