web-dev-qa-db-ja.com

tellg()関数がファイルのサイズを間違えましたか?

ファイルをバッファに読み込むサンプルプロジェクトを行いました。 tellg()関数を使用すると、ファイルから実際に読み取られる読み取り関数よりも大きな値が得られます。バグがあると思います。

ここに私のコードがあります:

編集:

void read_file (const char* name, int *size , char*& buffer)
{
  ifstream file;

  file.open(name,ios::in|ios::binary);
  *size = 0;
  if (file.is_open())
  {
    // get length of file
    file.seekg(0,std::ios_base::end);
    int length = *size = file.tellg();
    file.seekg(0,std::ios_base::beg);

    // allocate buffer in size of file
    buffer = new char[length];

    // read
    file.read(buffer,length);
    cout << file.gcount() << endl;
   }
   file.close();
}

メイン:

void main()
{
  int size = 0;
  char* buffer = NULL;
  read_file("File.txt",&size,buffer);

  for (int i = 0; i < size; i++)
    cout << buffer[i];
  cout << endl; 
}
24
Elior

tellgは、ファイルのサイズも、バイト単位の先頭からのオフセットも報告しません。後で同じ場所にシーク​​するために使用できるトークン値をレポートしますが、それ以上の値はありません。 (型を整数型に変換できることも保証されていません。)

少なくとも言語仕様に従って:実際には、Unixシステムでは、返される値はファイルの先頭からのバイト単位のオフセットになり、Windowsでは、ファイルの先頭からのオフセットになりますバイナリモードで開かれたファイルの場合。 Windows(およびほとんどの非Unixシステム)の場合、テキストモードでは、tellgが返すものとその位置に到達するために読み取る必要のあるバイト数の間の直接および即時マッピングはありません。 Windowsでは、実際に期待できるのは、値が読み取る必要のあるバイト数以上であるということです(ほとんどの場合、最大で2倍になりますが、それほど大きくなることはありません) )。

読み取ることができるバイト数を正確に知ることが重要である場合、確実にそうする唯一の方法は、読み取ることです。次のような方法でこれを実行できるはずです。

file.ignore( std::numeric_limits<std::streamsize>::max() );
std::streamsize length = file.gcount();
file.clear();   //  Since ignore will have set eof.
file.seekg( 0, std::ios_base::beg );

最後に、コードに関する他の2つのコメント:

まず、次の行:

*buffer = new char[length];

コンパイルしないでください:bufferchar*として宣言しているので、*bufferchar型を持ち、ポインターではありません。あなたがしているように見えることを考えると、おそらくbufferchar**として宣言したいでしょう。しかし、はるかに優れたソリューションは、std::vector<char>&またはstd::string&として宣言することです。 (そうすれば、サイズを返す必要もなくなり、例外が発生してもメモリリークが発生しなくなります。)

第二に、最後のループ条件が間違っています。一度に1文字ずつ読みたい場合は、

while ( file.get( buffer[i] ) ) {
    ++ i;
}

トリックを行う必要があります。より良い解決策は、おそらくデータのブロックを読み取ることです。

while ( file.read( buffer + i, N ) || file.gcount() != 0 ) {
    i += file.gcount();
}

あるいは:

file.read( buffer, size );
size = file.gcount();

編集:私はちょうど3番目のエラーに気づいた:あなたがファイルを開くことに失敗した場合、あなたは呼び出し元に伝えません。少なくとも、sizeを0に設定する必要があります(ただし、何らかの種類のより正確なエラー処理がおそらく優れています)。

55
James Kanze

C++ 17には_std::filesystem_ _file_size_メソッドと関数があり、タスク全体を合理化できます。

これらの関数/メソッドを使用すると、ファイルを開くのではなく、キャッシュされたデータを読み取ることができます(特に_std::filesystem::directory_entry::file_size_メソッドを使用)

これらの関数には、ディレクトリの読み取り権限のみが必要であり、ファイルの読み取り権限は必要ありません(tellg()のように)

4
fen
void read_file (int *size, char* name,char* buffer)
*buffer = new char[length];

これらの行はバグのように見えます。char配列を作成し、buffer [0] charに保存します。次に、バッファにファイルを読み込みますが、まだ初期化されていません。

bufferをポインターで渡す必要があります。

void read_file (int *size, char* name,char** buffer)
*buffer = new char[length];

または、参照により、C++の方法であり、エラーが発生しにくくなります。

void read_file (int *size, char* name,char*& buffer)
buffer = new char[length];
...
0
Arks