Russian Qt Forum

Qt => Общие вопросы => Тема начата: alex12 от Апрель 15, 2009, 21:43



Название: Вопрос про QFile::write()
Отправлено: alex12 от Апрель 15, 2009, 21:43
Всем привет!

Меня давно мучает вопрос:

Делаю запись в файл. Должен ли я ВСЕГДА делать вот так:
Код:
while( len )  {
  a = file.write( ptr, len );
  if( a < 0 ) break;
  ptr += a;
  len -= a;
}

или можно не заморачиваться:

Код:
file.write( ptr, len );

и забить на проверку количества реально записанных байт? В реальной жизни никогда не видел ошибок, если, конечно, не писать по 1GB за раз. Есть ли практический смысл в постоянной проверке реально записанных байт?


Название: Re: Вопрос про QFile::write()
Отправлено: Пантер от Апрель 15, 2009, 21:54
А если место на диске закончилось?


Название: Re: Вопрос про QFile::write()
Отправлено: lit-uriy от Апрель 15, 2009, 22:04
а зачем цикл?
Пытайся записать всё, проверь, если неполучилось, скажи об этом пользователю.


Название: Re: Вопрос про QFile::write()
Отправлено: whirlwind от Апрель 15, 2009, 22:05
не уверен, но по-моему, в linux'е запросто можно удалить файл, пока вы в него пишете.


Название: Re: Вопрос про QFile::write()
Отправлено: pastor от Апрель 15, 2009, 22:08
и забить на проверку количества реально записанных байт?

Забивать на проверку нестоит


Название: Re: Вопрос про QFile::write()
Отправлено: Пантер от Апрель 15, 2009, 22:21
не уверен, но по-моему, в linux'е запросто можно удалить файл, пока вы в него пишете.
Файл не будет удален, пока не закроется последний дескриптор. Т.е. писать в него можно будет, но при close() он исчезнет.


Название: Re: Вопрос про QFile::write()
Отправлено: alex12 от Апрель 15, 2009, 22:57
По-моему тут возможны несколько вариантов:

  • а) Файловая система всегда блокирует write до тех пор, пока все данные не уйдут на диск (в буфер ОС). Количество реально записанных байт всегда будет совпадать с требуемым.
  • б) Если файловая система сильно загружена, то она примет сколько сможет и возвратит количество байт меньше требуемого. Причем это не будет считаться ошибкой! Программист сам должен отслеживать такую ситуацию.
  • в) Произошла ошибка ФС (например, закончилось место) и вызов завершается с -1.

Случай в) не рассматриваем ибо тут все понятно. Вопрос в том, как себя ведет QFile? По вариану а) или б)? Вот, например, QTcpSocket ведет себя по сценарию б). Если нужно отправить много данных, то нужно обязательно следить сколько байт ушло в линию.

С сделал простенький тест. Записываю оооочень большой файл блоками разной длины и в случае чего вывожу на консоль.
Так вот, практика показывает, что я не смог получить ни разу ситуации б). Всегда все работает по а). Система WinXP MinGW Qt 4.5.0.

Есть ли теоретическое обоснование этого факта? Как обстоят дела на других платформах?

Код:
#include <QtCore>

#define BLOCK     10*1024*1024
#define TOTAL   1024*1024*1024

int main(int argc, char *argv[])
{
  int i,a;
  QByteArray ba;
  ba.resize( BLOCK );
 
  QFile file("out.dat"); 
  file.open( QIODevice::WriteOnly | QIODevice::Truncate );
 
  for( i=0; i<((TOTAL)/(BLOCK)); i++ )  {
    a = file.write( ba );
    if( a != BLOCK ) qDebug() << a;
  }

  return 0;
}


Название: Re: Вопрос про QFile::write()
Отправлено: Пантер от Апрель 15, 2009, 23:01
На сколько я знаю, варианта Б не может быть как такового - или записано все, или ничего.


Название: Re: Вопрос про QFile::write()
Отправлено: Alex Custov от Апрель 16, 2009, 01:55
+

http://www.linux.org.ru/view-message.jsp?msgid=3591104&lastmod=1238980775134


Название: Re: Вопрос про QFile::write()
Отправлено: mugabe от Апрель 16, 2009, 04:29
qfsfileengine_win.cpp:
Код
C++ (Qt)
qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
{
   Q_Q(QFSFileEngine);
 
   if (fh || fd != -1) {
       // stdio / stdlib mode.
       return writeFdFh(data, len);
   }
 
   // Windows native mode.
   if (fileHandle == INVALID_HANDLE_VALUE)
       return -1;
 
   qint64 bytesToWrite = DWORD(len); // <- lossy
 
   // Writing on Windows fails with ERROR_NO_SYSTEM_RESOURCES when
   // the chunks are too large, so we limit the block size to 32MB.
   static const DWORD maxBlockSize = 32 * 1024 * 1024;
 
   qint64 totalWritten = 0;
   do {
       DWORD blockSize = qMin<DWORD>(bytesToWrite, maxBlockSize);
       DWORD bytesWritten;
       if (!WriteFile(fileHandle, data + totalWritten, blockSize, &bytesWritten, NULL)) {
           if (totalWritten == 0) {
               // Note: Only return error if the first WriteFile failed.
               q->setError(QFile::WriteError, qt_error_string());
               return -1;
           }
           break;
       }
       if (bytesWritten == 0)
           break;
       totalWritten += bytesWritten;
       bytesToWrite -= bytesWritten;
   } while (totalWritten < len);
   return qint64(totalWritten);
}
 


qfsfileengine.cpp:
Код
C++ (Qt)
qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len)
{
   Q_Q(QFSFileEngine);
   qint64 result;
   qint64 written = 0;
 
   do {
       // Write blocks of 4k to avoid platform limitations (Windows commonly
       // bails out if you read or write too large blocks at once).
       qint64 bytesToWrite = qMin<qint64>(4096, len - written);
       if (fh) {
           do {
               // Buffered stdlib mode.
               result = qint64(fwrite(data + written, 1, size_t(bytesToWrite), fh));
           } while (result == 0 && errno == EINTR);
           if (bytesToWrite > 0 && result == 0)
               result = -1;
       } else {
           do {
               // Unbuffered stdio mode.
               result = QT_WRITE(fd, data + written, bytesToWrite);
           } while (result == -1 && errno == EINTR);
       }
       if (result > 0)
           written += qint64(result);
   } while (written < len && result > 0);
 
   // If we read anything, return that with success. Otherwise, set an error,
   // and return the last return value.
   if (result > 0)
       return written;
   q->setError(errno == ENOSPC ? QFile::ResourceError : QFile::WriteError, qt_error_string(errno));
   return result;
}
 

делать первым способом не только не обязательно, но и вредно.
если, конечно, не собираетесь отслеживать процесс записи в файл.


Название: Re: Вопрос про QFile::write()
Отправлено: alex12 от Апрель 16, 2009, 11:07
qfsfileengine_win.cpp:
Код
C++ (Qt)
qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len)
...
 
qfsfileengine.cpp:
Код
C++ (Qt)
qint64 QFSFileEnginePrivate::writeFdFh(const char *data, qint64 len)
...
 
...
делать первым способом не только не обязательно, но и вредно.
если, конечно, не собираетесь отслеживать процесс записи в файл.

Спасибо. Теперь все понятно. QFile гарантирует запись всех данных и сам делает циклическую запись. Жаль, что этого нигде не написано явно в документации. Или я просто не нашел...