Wczytywanie danych z pliku, śmieci w strumieniu wyjściowym

0

Mam pytanie dotyczące prostego wczytywania danych z pliku i wyrzucania ich na ekran:

// reading an entire binary file
#include <iostream>
#include <fstream>
using namespace std;

int main () {
  streampos size;
  char * memblock;

  ifstream file ("example.bin", ios::in|ios::binary|ios::ate);
  if (file.is_open())
  {
    size = file.tellg();
    memblock = new char [size];
    file.seekg (0, ios::beg);
    file.read (memblock, size);
    file.close();

    cout << "the entire file content is in memory" << endl;
    cout << memblock << endl;;

    delete[] memblock;
  }
  else cout << "Unable to open file";
  return 0;
}

Powyższy kod jest skopiowany z tutoriala (http://www.cplusplus.com/doc/tutorial/files/).
Dodałem tylko linijkę wypisywania na ekran:

cout << memblock << endl;

Zawsze wypisuje mi o 1 znak więcej niż ma size, wywala jakieś śmieci. Rozumiem, że on wczytuje zawsze size + 1 jako miejsce na znak końca pliku i później nie ma sprawdzania tego w cout i wywala cały bufor, dobrze rozumiem?
Jak za pomocą cout wypisać bufor z takim rozmiarem jak size? Wiem że mogę sobie ostatni znak urwać za pomocą pętli ale po co do tego zaprzęgać pętlę?

0
file.seekg(0, ios::end) ;

Tę linijkę dodaj przed wywołaniem metody tellg().

Aha, i zamień streampos size na int size oraz wywal tę flagę ios::ate z metody open.

0

Dodałem tak:

...
    file.seekg(0, ios::end) ;
    size = file.tellg();
...

Mimo to nadal na wyjściu jest słowo z pliku + jakiś śmieciowy znak z pamięci?

0

Zastosowałem się do wszystkich powyższych porad i nadal to samo, korzystajac z:

cout << memblock << endl;

mam śmieci na końcu bufora.

po zmianie na:

cout.write(memblock, size) << endl;

dopiero jest poprawnie wypisane, bez śmieci.

2

memblock to tablica char. std::cout wypisuje taka tablicę aż do napotkania znaku '\0', którego jak widać nie ma w tym miejscu, w którym powinien być. To normalne, że go nie ma, jeżeli wczytałeś te dane z pliku. Żeby takie wypisywanie zadziałało musisz sam dodać ten znak '\0':

memblock = new char [size + 1]; // +1 dla znaku '\0'.
// ...
memblock[size] = '\0';
// ...

Możesz też zainicjalizować tablicę memblock zerami. Ponieważ wczytujesz size znaków, dodatkowy znak zostanie zerem.

memblock = new char [size + 1]();
// ...
1

W poście wyżej masz poprawne rozwiązanie, ale jak nie chcesz zmieniać rozmiaru memblock to zawsze możesz wyświetlić znak po znaku:

for(streampos i=0;i<size;++i) cout<<memblock[i];
cout<<endl;
0

Dzięki, działa. Z tym, że jeżeli zostawię:

streampos size;

to przy kompilacji mam warning'a

fprintf.cpp:18:30: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better 
than the worst conversion for the second: [enabled by default]                                                              
  memblock = new char [size + 1]; //+1 dla znaku '\0'                                                                       
                              ^                                                                                             
In file included from c:\mingw\lib\gcc\mingw32\4.8.1\include\c++\iosfwd:40:0,                                               
                 from c:\mingw\lib\gcc\mingw32\4.8.1\include\c++\ios:38,                                                    
                 from c:\mingw\lib\gcc\mingw32\4.8.1\include\c++\ostream:38,                                                
                 from c:\mingw\lib\gcc\mingw32\4.8.1\include\c++\iostream:39,                                               
                 from fprintf.cpp:1:                                                                                        
c:\mingw\lib\gcc\mingw32\4.8.1\include\c++\bits\postypes.h:178:7: note: candidate 1: std::fpos<_StateT> std::fpos<_StateT>::
operator+(std::streamoff) const [with _StateT = int; std::streamoff = long long int]                                        
       operator+(streamoff __off) const                                                                                     
       ^                                                                                                                    
fprintf.cpp:18:30: note: candidate 2: operator+(std::streamoff {aka long long int}, int) <built-in>                         
  memblock = new char [size + 1]; //+1 dla znaku '\0'                                                                       

Jak zamienię na

int size;

kompilacja przebiega bez problemów.
Mam GCC ver 4.8.1. Który wybór w takim wypadku jest lepszy?

1

Najlepszy to:

streampos filesize=file.tellg();
size_t size=(size_t)filesize;
if(filesize==size)
  {
  }
else ; // powyżej 4GB tak czy owak zabraknie pamięci
1

A jak myślisz - warningi oznaczają poprawny kod, czy nie?

0

Endrju, jak zrobię static_cast<streampos>(1) to jest wszystko ok.
furious programming, wiem że jest niepoprawny i zawsze warningi powinny być usuwane, dlatego też dałem znać. Nie chodziło mi o to, żeby wybierać między kodem z streampos i warningiem, a intem bez warningu (choć mogło to tak zabrzmieć).
_13th_Dragon, Twój kod też śmiga jak powinien.
Dzięki za pomoc wszystkim.

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