Файл для чтения и записи с использованием MFC CFile

Мне интересно, почему приведенный ниже код не записывает правильные данные в файл. Если я изменю размер буфера на большее значение, этот код будет работать нормально.

Для приведенного ниже кода, если я пытаюсь прочитать файл размером менее 500 байт, он работает хорошо, но для большего файла мне просто нужно увеличить буфер. Чего мне не хватает в цикле чтения?

  const int iBuffSiz = 500;
  char chBuffer[iBuffSiz];
  memset(chBuffer, 0, sizeof(chBuffer));
    CFile file;
  CFile fileO;

  if(file.Open(XML_FILE_NAME, CFile::modeRead | CFile::typeBinary) == FALSE)
  {
    return;
  }

  if(fileO.Open(XML_FILE_NAME_O, CFile::modeCreate | CFile::modeWrite | CFile::typeBinary) == FALSE)
  {
    return;
  }

  while(file.Read(chBuffer, iBuffSiz) > 0)
  {
    try{
      UINT iCount = strlen(chBuffer);
      fileO.Write(chBuffer, iCount);
    }
    catch (CFileException *exp)
    {
      TCHAR szCause[255];
      exp->GetErrorMessage(szCause, 255);
    }
  }

  //Closing file handle and socket after complete file send
  file.Close();
  fileO.Close();

person hypheni    schedule 17.03.2015    source источник
comment
Вы открываете входной файл как CFile::typeBinary, но используете strlen(), чтобы получить длину чего угодно.   -  person plaintext    schedule 17.03.2015
comment
Это немного вводит в заблуждение, @plaintext, поскольку CFile::typeBinary не имеет ничего общего с проблемами. Проблема скорее в том, что у OP нет гарантии, с этим флагом или без него, что содержимое буфера представляет собой строку с нулевым завершением, которая требуется для использования strlen(). Вместо этого проверьте документацию CFile, я думаю, она может сказать вам, сколько байт она прочитала. Наконец, еще два совета: есть лучшие способы скопировать файл (поищите в Интернете!), а перехват, а затем полное игнорирование исключений очень вредно.   -  person Ulrich Eckhardt    schedule 17.03.2015


Ответы (2)


CFile::Read не завершает буфер нулевым значением, поэтому strlen буфера может быть больше, чем буфер, он приводит к переполнению буфера. Есть способ обойти это, но strlen все равно не будет работать с бинарными файлами, на этот раз буфер урезан слишком коротко. Так что лучше использовать значение, возвращаемое CFile::Read

UINT iCount;
while( ( iCount = file.Read(chBuffer, iBuffSiz) ) > 0 )
{
   try
   {
      fileO.Write(chBuffer, iCount);
   }
   catch (CFileException *exp)
   {
      TCHAR szCause[255];
      exp->GetErrorMessage(szCause, 255);
   }
}
person Barmak Shemirani    schedule 18.03.2015
comment
Это работает идеально, и причина очевидна. Спасибо. Только одно, что мне любопытно узнать. Если я изменю цикл while на «пока ( iCount = file.Read (chBuffer, iBuffSiz) › 0)», почему он не может записать правильные данные в выходной файл. ›0 должен выполнять ту же работу, что и вышеприведенный код, а цикл будет выполняться до тех пор, пока значение не станет истинным. В чем здесь проблема. - person hypheni; 18.03.2015
comment
Требуется еще один набор скобок для переноса результата оператора =. Я отредактировал ответ с помощью > 0. Я почти уверен, что CFile::Read не возвращает отрицательных чисел, но лучше перестраховаться. И раньше это сбивало с толку. - person Barmak Shemirani; 18.03.2015

Согласитесь с Бармарком, что CFile::Read может возвращать счетчик, превышающий указанный размер буфера.

Еще один момент, который у меня есть, это

*** Вам необходимо очистить буфер после его записи в другой файл, так как он указывает на строку, которую вы уже записали в целевой файл, и из-за этого вы можете не получить точную копию желаемого файла.

  while(file.Read(chBuffer, iBuffSiz-1) > 0)
  {
    try{
      UINT iCount = strlen(chBuffer);
      fileO.Write(chBuffer, iCount);
      memset(chBuffer, 0, sizeof(chBuffer));
    }
    catch (CFileException *exp)
    {
      TCHAR szCause[255];
      exp->GetErrorMessage(szCause, 255);
    }
  }
person User420    schedule 18.03.2015
comment
CFile::Read вернет максимальное количество байтов, указанное вами в качестве второго параметра, это strlen в буфере, не заканчивающемся нулем, что приводит к неопределенному поведению. Кроме того, вам не нужно очищать буфер, если вы используете возвращаемое значение CFile::Read, чтобы определить, какой объем буфера вы можете использовать, а какой должен быть отброшен. - person Fabio C.; 12.11.2018