Убедитесь, что все байты правильно записаны в файл

Можно ли проверить, действительно ли все байты записываются в QFile или нет? На данный момент это все, что у меня есть

QFile f(name);
if (f.open(QIODevice::WriteOnly)){
     f.write(bytes);
}

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


person BurninatorDor    schedule 07.10.2014    source источник


Ответы (5)


Вам нужна контрольная сумма, с помощью которой вы можете проверить целостность ваших данных. Здесь вам нужно использовать qChecksum следующим образом:

QFile f(name);
if (f.open(QIODevice::ReadWrite)) {
     f.write(bytes);
}

quint16 fileCheckSum = qChecksum(bytes.data(), bytes.length());

if (f.open(QIODevice::ReadWrite)) {
    QByteArray writtenBytes = f.readAll();

    quint16 writtenBytesCheckSum = qChecksum(writtenBytes .data(), writtenBytes .length());

    if(fileCheckSum == writtenBytesCheckSum)
    {
        qDebug() << "File is valid.";
    }
    else
    {
        qDebug() << "File is corrupt.";
    }
}

Я не компилировал код, но он должен работать. Если это не так, я буду более конкретным с примером.

person Jacob Krieg    schedule 07.10.2014
comment
Спасибо! Именно то, что я искал - person BurninatorDor; 08.10.2014
comment
Конечно, это не гарантирует, что файл действительно попадет на диск, так как вы вычисляете контрольную сумму кеша, и не гарантирует, что в случае сбоя записи исходный файл не будет уничтожен. - person Kuba hasn't forgotten Monica; 09.10.2014

В Qt 5 вам действительно следует использовать QSaveFile. Он обеспечивает два очень важных инварианта:

  1. частичная/неудачная запись не повреждает существующий файл,

  2. файл сбрасывается к моменту уничтожения экземпляра QSaveFile.

Поскольку это правильный класс C++, реализующий RAII, вам не нужно делать ничего особенного, чтобы убедиться, что он работает, кроме вызова commit(). Значение commit() таково: вы указываете, что вы не будете записывать больше никаких данных в файл. На этом этапе реализация может закрыть файл, сбросить его на диск и заменить старый файл новым.

/// When this function returns true, you can be certain that the file contains exactly "foo bar".
bool writeFooBar() {
  QSaveFile file(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
  if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    return false;
  if (-1 == file.write("foo bar"))
    return false;
  return file.commit();
}
person Kuba hasn't forgotten Monica    schedule 08.10.2014
comment
Работает ли это на планшете Android и есть ли высокая вероятность сбоя питания? - person Muhammet Ali Asan; 21.03.2017
comment
Я предполагаю, что да, это Linux, и я предполагаю, что Qt использует обычную библиотеку Linux C для доступа к файловой системе. - person Kuba hasn't forgotten Monica; 21.03.2017

Если вас беспокоит запись поврежденных файлов, возможно, QSaveFile будет лучше использовать класс вместо QFile.

Как указано в документации: -

QSaveFile — это устройство ввода-вывода для записи текстовых и двоичных файлов без потери существующих данных в случае сбоя операции записи.

person TheDarkKnight    schedule 08.10.2014

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

Пожалуйста, изучите SHA-1 (Алгоритм безопасного хэширования), MD5 и "Хеш-функции". Также «алгоритм целостности данных С++».

person Thomas Matthews    schedule 07.10.2014
comment
Может быть, это поможет ОП. В Qt уже есть класс для md5 и других: qt-project.org/doc/ qt-5/QCryptographicHash.html - person Kosovan; 08.10.2014
comment
Спасибо за ссылку @Chernobyl. На данный момент я получаю SHA1 для каждого bytes. Я просто не слишком знаком с процессом, о котором говорит @ThomasMatthews. Я думаю, мне нужно будет продолжить исследование и вернуться к нему. - person BurninatorDor; 08.10.2014

QFile::flush или QFile::close должны вызывать запись всего содержимого буфера. Важно проверять возвращаемые значения всех вызовов QFile.

person James Roth    schedule 07.10.2014
comment
В моем приложении я пишу несколько bytes по 1 МБ каждый. Скажем, после добавления 10 МБ данных, если я сделаю f.flush(), я узнаю, что определенный блок неполный? Смогу ли я выяснить, какой конкретно блок? - person BurninatorDor; 07.10.2014
comment
write, flush или close вернут false, если системный вызов нижнего уровня завершится ошибкой. Например, если больше нет места на диске. Если вы этому не доверяете, возможно, из-за того, что другой процесс или поток копирует ваш файл, вы можете закрыть файл, открыть его только для чтения и сравнить с байтами. Что, если файл станет поврежденным после этого теста? В какой-то момент вы должны доверять возвращаемым значениям. - person James Roth; 08.10.2014