Функция 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; 
}

Ответ 1

tellg не сообщает размер файла, ни смещение от начала в байтах. Он сообщает значение токена, которое может позже можно использовать для поиска того же места и не более того. (Он даже не гарантирует, что вы можете преобразовать тип в интегральный тип.)

По крайней мере, согласно языковой спецификации: на практике, в Unix-системах возвращаемое значение будет смещением в байтах с самого начала файла, а под Windows это будет смещение от начала файла для файлов, открытых в двоичный режим. Для Windows (и большинства не-Unix-систем) в тексте режиме, нет прямого и непосредственного отображения между тем, что tellg возвращает и количество байтов, которое вы должны прочитать, чтобы добраться до это положение. В Windows все, на что вы действительно можете рассчитывать, это что значение будет не меньше, чем количество байт, которое у вас есть читать (и в большинстве реальных случаев не будет слишком большим, хотя это может быть в два раза больше).

Если важно точно знать, сколько байтов вы можете прочитать, единственным способом надежного выполнения этого является чтение. Вы должны быть в состоянии сделать это с чем-то вроде:

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 );

Наконец, еще два замечания относительно вашего кода:

Сначала строка:

*buffer = new char[length];

не следует компилировать: вы объявили buffer как char*, поэтому *buffer имеет тип char и не является указателем. Учитывая, что вы, кажется, делаете, вы, вероятно, хотите объявить buffer как a char**. Но гораздо лучшим решением было бы объявить его как std::vector<char>& или std::string&. (Таким образом, вы также не нужно возвращать размер, и вы не будете утечки памяти если есть исключение.)

Во-вторых, условие цикла в конце неверно. Если вы действительно хотите читать по одному символу за раз,

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();

EDIT: я заметил третью ошибку: если вы не открыли файл, вы не говорите вызывающему. По крайней мере, вы должны установите size на 0 (но какая-то более точная ошибка обработка, вероятно, лучше).

Ответ 2

В С++ 17 есть методы и функции std::filesystem file_size, которые могут упростить всю задачу.

С помощью этих функций/методов есть шанс не открывать файл, а читать кэшированные данные (особенно с помощью метода std::filesystem::directory_entry::file_size)

Эти функции также требуют только разрешения на чтение каталога, а не разрешения на чтение файла.

Ответ 3

fseek(fptr, 0L, SEEK_END);
filesz = ftell(fptr);

сделает файл, если файл открыт через fopen

используя ifstream,

in.seekg(0,ifstream::end);
dilesz = in.tellg();

будет делать аналогичные

Ответ 4

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

Эти строки выглядят как ошибка: вы создаете массив char и сохраняете буфер [0] char. Затем вы читаете файл в буфер, который все еще не инициализирован.

Вам нужно передать buffer указателем:

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

Или по ссылке, которая является способом С++ и менее подвержена ошибкам:

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