QDataStream 读取和写入的字节数比 QFile::length() 报告的要多

Posted

技术标签:

【中文标题】QDataStream 读取和写入的字节数比 QFile::length() 报告的要多【英文标题】:QDataStream reads and writes more bytes than QFile::length() reports to have 【发布时间】:2020-06-23 00:54:49 【问题描述】:

我有一个实用程序可以将文件从一个位置复制到另一个位置。

我遇到的问题是,当使用QDataStream 读取 X 字节并写入它时,正在读取/写入的字节数超过了文件的字节数。我发现很多文件都会出现这个问题。

我正在使用QDataStream::readRawData() 和QDataStream::writeRawData() 来方便读取/写入文件,如下所示

 QDataStream in(&sourceFile);
 QDataStream out(&newFile);

 // Read/Write byte containers
 qint64 fileBytesRead = 0;
 quint64 fileBytesWritten = 0;
 qint64 bytesWrittenNow = 0;

 quint8* buffer = new quint8[bufSize];
 while ((fileBytesRead = in.readRawData((char*)buffer, bufSize)) != 0) 

      // Check if we have a read/write mismatch
      if (fileBytesRead == -1) 
           printCritical(TAG, QString("Mismatch read/write: [R:%1/W:%2], total file write/max [W:%3/M:%4]. File may be corrupted, skipping...").arg(QString::number(fileBytesRead), QString::number(bytesWrittenNow), QString::number(fileBytesWritten), QString::number(storageFile.size)));

           // close source file handle
           sourceFile.close();

           // Close file handle
           newFile.close();

           return BackupResult::IOError;
      

      // Write buffer to file stream
      bytesWrittenNow = out.writeRawData((const char*)buffer, fileBytesRead);

      // Check if we have a read/write mismatch
      if (bytesWrittenNow == -1) 
           printCritical(TAG, QString("Mismatch read/write: [R:%1/W:%2], total file write/max [W:%3/M:%4]. File may be corrupted, skipping...").arg(QString::number(fileBytesRead), QString::number(bytesWrittenNow), QString::number(fileBytesWritten), QString::number(storageFile.size)));

           // close source file handle
           sourceFile.close();

           // Close file handle
           newFile.close();

           return BackupResult::IOError;
      

      // Add current buffer size to written bytes
      fileBytesWritten += bytesWrittenNow;

      if(fileBytesWritten > storageFile.size) 
          qWarning() << "Extra bytes read/written exceeding file length";    <================= this line is hit every now and then
      

      //...

这个问题并不一致,但时不时会发生,我不知道为什么。有人对可能的原因有想法吗?

【问题讨论】:

没有回答您的问题,但是...如果可以使用c++17,为什么不直接使用QFile::copy 甚至std::filesystem::copy_file 【参考方案1】:

函数QDataStream::writeRawData() 的名称听起来很适合编写二进制数据。不幸的是,这只是故事的一半。

文件的打开模式在某些条件下也是相关的——例如如果QFileWindows 上用QIODevice::Text 打开:

QIODevice::Text

读取时,行尾终止符被转换为'\n'。写入时,行尾终止符被转换为本地编码,例如 Win32 的 '\r\n'。

我准备了一个MCVE 来证明:

// Qt header:
#include <QtCore>

void write(const QString &fileName, const char *data, size_t size, QIODevice::OpenMode mode)

  qDebug() << "Open file" << fileName;
  QFile qFile(fileName);
  qFile.open(mode | QIODevice::WriteOnly);
  QDataStream out(&qFile);
  const int ret = out.writeRawData(data, size);
  qDebug() << ret << "bytes written.";


// main application
int main(int argc, char **argv)

  const char data[] = 
    '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07',
    '\x08', '\x09', '\x0a', '\x0b', '\x0c', '\x0d', '\x0e', '\x0f'
  ;
  const size_t size = sizeof data / sizeof *data;
  write("data.txt", data, size, 0);
  write("test.txt", data, size, QIODevice::Text);

在 Windows 10 上的 VS2017 中构建和测试:

Open file "data.txt"
16 bytes written.
Open file "test.txt"
16 bytes written.

在cygwin 的帮助下检查结果:

$ ls -l *.txt
-rwxrwx---+ 1 scheff Domänen-Benutzer 427 Jun 23 08:24 CMakeLists.txt
-rwxrwx---+ 1 scheff Domänen-Benutzer  16 Jun 23 08:37 data.txt
-rwxrwx---+ 1 scheff Domänen-Benutzer  17 Jun 23 08:37 test.txt

$

data.txt 有 16 个字节,但 test.txt 有 17 个字节。糟糕!

$ hexdump -C data.txt
00000000  00 01 02 03 04 05 06 07  08 09 0a 0b 0c 0d 0e 0f  |................|
00000010

$ hexdump -C test.txt
00000000  00 01 02 03 04 05 06 07  08 09 0d 0a 0b 0c 0d 0e  |................|
00000010  0f                                                |.|
00000011

$

显然,底层的 Windows 文件函数将\n“纠正”为\r\n09 0a 0b 变为09 0d 0a 0b。因此,会出现一个额外的字节,该字节不是最初写入的数据的一部分。

QFile 被打开以供阅读并涉及QIODevice::Text 时,可能会发生类似的效果。

【讨论】:

以上是关于QDataStream 读取和写入的字节数比 QFile::length() 报告的要多的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PySide 和 Python 3.X 中将字节写入 QDataStream?

计算 Qt5 中写入 QDataStream 的字节数

如何从 QDataStream 中读取数组

qfile获取文件第一行

读取 QDataStream 中的特定对象并计算存储的对象数

QT中关于Qdatastream使用问题 socket 读数据同时写入file内