Qt:如何使用 QT 复制大数据?

Posted

技术标签:

【中文标题】Qt:如何使用 QT 复制大数据?【英文标题】:Qt: How can I copy a big data using QT? 【发布时间】:2018-04-05 12:47:30 【问题描述】:

我想读取一个大数据,然后使用 Qt 将其写入一个新文件。

我试图读取一个大文件。而且大文件只有一行。我使用readAll()readLine() 进行测试。

如果数据文件600MB左右,我的代码虽然慢,但可以运行。

如果数据文件大约6GB,我的代码会失败。

你能给我一些建议吗?

更新 我的测试代码如下:

#include <QApplication>
#include <QFile>
#include <QTextStream>
#include <QTime>
#include <QDebug>
#define qcout qDebug()

void testFile07()

    QFile inFile("../03_testFile/file/bigdata03.txt");
    if (!inFile.open(QIODevice::ReadOnly | QIODevice::Text))
    
        qcout << inFile.errorString();
        return ;
    

    QFile outFile("../bigdata-read-02.txt");
    if (!outFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
        return;

    QTime time1, time2;
    time1 = QTime::currentTime();
    while(!inFile.atEnd())
    
        QByteArray arr = inFile.read(3*1024);
        outFile.write(arr);
    
    time2 = QTime::currentTime();
    qcout << time1.msecsTo(time2);


void testFile08()

    QFile inFile("../03_testFile/file/bigdata03.txt");
    if (!inFile.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    QFile outFile("../bigdata-readall-02.txt");
    if (!outFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
        return;

    QTime time1, time2, time3;
    time1 = QTime::currentTime();

    QByteArray arr = inFile.readAll();
    qcout << arr.size();
    time3 = QTime::currentTime();
    outFile.write(inFile.readAll());

    time2 = QTime::currentTime();
    qcout << time1.msecsTo(time2);



int main(int argc, char *argv[])

    testFile07();
    testFile08();

    return 0;

测试后,我分享一下我的经验。

read()readAll() 似乎都一样快;更确切地说,read() 稍微快一些。 真正的区别在于写作。

文件大小为600MB: 使用read函数,读写文件耗时2.1s,读取875ms 使用readAll函数,读写文件耗时10s,读取907ms

文件大小为6GB: 使用read函数,读写文件耗时162s,读取58s 使用readAll函数,得到错误答案0。Fail运行良好。

【问题讨论】:

请发minimal reproducible example。 我在Qt 中使用的文件是您提到的大小的 10 倍。话虽如此,这是来自本地磁盘。过去,我在 Windows 客户端上通过 samba 网络共享读取类似大小的文件(几百 MB)时遇到问题。在那种情况下,我不得不将阅读分成更小的块。 不要使用readAll()。对于那些懒得思考如何将输入作为流处理的程序员来说,这只是一个快速的技巧。如果您真的非常需要随机访问整个文件,请考虑对其进行内存映射(如果原生 mmap() 便携性不足,您可以使用 boost::interprocess)。 @JesperJuhl Buddy,我已经编辑了我的帖子。现在可以了吗? 您应该在 read() 调用中使用不同的缓冲区大小进行基准测试,以找出最佳性能的缓冲区。 【参考方案1】:

将这两个文件作为 QFiles 打开。在一个循环中,read 一个固定数量的字节,比如 4K,从输入文件到一个数组,然后write 那个数组到输出文件。继续,直到用完字节。

但是,如果您只想逐字复制文件,可以使用QFile::copy

【讨论】:

【参考方案2】:

您可以使用QFile::map 并使用指向映射内存的指针一次性写入目标文件:

void copymappedfile(QString in_filename, QString out_filename)

    QFile in_file(in_filename);
    if(in_file.open(QFile::ReadOnly))
    
        QFile out_file(out_filename);
        if(out_file.open(QFile::WriteOnly))
        
            const qint64 filesize = in_file.size();
            uchar * mem = in_file.map(0, filesize, QFileDevice::MapPrivateOption);
            out_file.write(reinterpret_cast<const char *>(mem) , filesize);
            in_file.unmap(mem);

            out_file.close();
        
        in_file.close();
    

【讨论】:

我已经测试了你的代码。当文件大小为600MB时,复制文件只需1.6s左右。伟大的!当文件大小为6GB时,代码失败。【参考方案3】:

要记住的一点: 使用read(),您可以为当前读取的块指定最大大小(在您的示例中为 3*1024 字节),使用readAll(),您告诉程序一次读取整个文件。

在第一种情况下,您(反复)将 3072 字节放入堆栈,写入它们,一旦当前循环迭代结束,它们就会从堆栈中删除。在第二种情况下,您将整个文件压入堆栈。一次向堆栈推送 600MB 可能是导致性能问题的原因。如果您尝试一次将 6GB 放入堆栈,您可能会用完内存/地址空间 - 导致您的程序崩溃。

【讨论】:

以上是关于Qt:如何使用 QT 复制大数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 QTcpSocket 监听 qt 中的特定端口? [复制]

如何在Qt中使用自定义数据类型

你如何在 Qt 中使用 unicode? [复制]

QT中如何实现界面之间传递数据??

如何在 Qt 中从 txt 文件中加载大数据

如何在 Qt 中绘制曲线? [复制]