将文件拆分为几个部分

时间:2014-03-23 06:24:58

标签: c++ qt split copy

我正在尝试在Qt中创建文件复制应用程序。我需要将源文件拆分为多个文件,以便我的复制功能可以逐个复制部分数据并更新QProgressBar。要准确更新进度,我需要将源文件拆分为原始大小的1%。我的做法是错的。我无法在这个主题上找到太多资源。如何将源文件分成几个相同大小的部分?

2 个答案:

答案 0 :(得分:0)

通常使用进度条进行文件复制是以这种方式完成的:

  1. 打开源文件(供读取)
  2. 打开目标文件(用于写入)
  3. 从源文件中读取一个块(64 kB左右)并将其写入目标文件
  4. 更新进度条
  5. 重复步骤3.和4.直到源文件结束
  6. done :)关闭文件
  7. 无需将文件拆分为多个文件。只需按小块处理文件(块后块),而不是一次整个文件。

    “在复制之前将文件拆分成多个文件”方法是错误的 - 这种拆分方法与复制一样昂贵,整个操作需要两倍的时间,而且你需要在拆分过程中更新进度条。

答案 1 :(得分:0)

以下是这种异步文件复制器的独立草图。有一些缺点:

  1. 错误报告也需要报告QFile错误代码。

  2. 真正需要一个自定义互斥锁柜台来处理互斥锁RAII。

  3. 查看代码时,请确保单独考虑实现和接口(使用)。界面使其易于使用。实现的相对复杂性使得易于使用的界面成为可能。

    #include <QApplication>
    #include <QByteArray>
    #include <QProgressDialog>
    #include <QFileDialog>
    #include <QBasicTimer>
    #include <QElapsedTimer>
    #include <QThread>
    #include <QMutex>
    #include <QFile>
    #include <limits>
    
    class FileCopier : public QObject {
       Q_OBJECT
       QMutex mutable m_mutex;
       QByteArray m_buf;
       QBasicTimer m_copy, m_progress;
       QString m_error;
       QFile m_fi, m_fo;
       qint64 m_total, m_done;
       int m_shift;
    
       void close() {
          m_copy.stop();
          m_progress.stop();
          m_fi.close();
          m_fo.close();
          m_mutex.unlock();
       }
       /// Takes the error string from given file and emits an error indication.
       /// Closes the files and stops the copy. Always returns false
       bool error(QFile & f) {
          m_error = f.errorString();
          m_error.append(QStringLiteral("(in %1 file").arg(f.objectName()));
          emit finished(false, m_error);
          close();
          return false;
       }
       void finished() {
          emitProgress();
          emit finished(m_done == m_total, m_error);
          close();
       }
       void emitProgress() {
          emit progressed(m_done, m_total);
          emit hasProgressValue(m_done >> m_shift);
       }
       void timerEvent(QTimerEvent * ev) {
          if (ev->timerId() == m_copy.timerId()) {
             // Do the copy
             qint64 read = m_fi.read(m_buf.data(), m_buf.size());
             if (read == -1) { error(m_fi); return; }
             if (read == 0) return finished();
             qint64 written = m_fo.write(m_buf.constData(), read);
             if (written == -1) { error(m_fo); return; }
             Q_ASSERT(written == read);
             m_done += read;
          }
          else if (ev->timerId() == m_progress.timerId())
             emitProgress();
       }
       Q_INVOKABLE void cancelImpl() {
          if (!m_fi.isOpen()) return;
          m_error = "Canceled";
          finished();
       }
    public:
       explicit FileCopier(QObject * parent = 0) :
          QObject(parent),
          // Copy 64kbytes at a time. On a modern hard drive, we'll copy
          // on the order of 1000 such blocks per second.
          m_buf(65536, Qt::Uninitialized)
       {
          m_fi.setObjectName("source");
          m_fo.setObjectName("destination");
       }
       /// Copies a file to another with progress indication.
       /// Returns false if the files cannot be opened.
       /// This method is thread safe.
       Q_SLOT bool copy(const QString & src, const QString & dst) {
          bool locked = m_mutex.tryLock();
          Q_ASSERT_X(locked, "copy",
                     "Another copy is already in progress");
          m_error.clear();
    
          // Open the files
          m_fi.setFileName(src);
          m_fo.setFileName(dst);
          if (! m_fi.open(QIODevice::ReadOnly)) return error(m_fi);
          if (! m_fo.open(QIODevice::WriteOnly)) return error(m_fo);
          m_total = m_fi.size();
          if (m_total < 0) return error(m_fi);
    
          // File size might not fit into an integer, calculate the number of
          // binary digits to shift it right by. Recall that QProgressBar etc.
          // all use int, not qint64!
          m_shift = 0;
          while ((m_total>>m_shift) >= std::numeric_limits<int>::max()) m_shift++;
          emit hasProgressMaximum(m_total>>m_shift);
    
          m_done = 0;
          m_copy.start(0, this);
          m_progress.start(100, this); // Progress is emitted at 10Hz rate
          return true;
       }
       /// This method is thread safe only when a copy is not in progress.
       QString lastError() const {
          bool locked = m_mutex.tryLock();
          Q_ASSERT_X(locked, "lastError",
                     "A copy is in progress. This method can only be used when"
                     "a copy is done");
          QString error = m_error;
          m_mutex.unlock();
          return error;
       }
       /// Cancels a pending copy operation. No-op if no copy is underway.
       /// This method is thread safe.
       Q_SLOT void cancel() {
          QMetaObject::invokeMethod(this, "cancelImpl");
       }
       /// Signal for progress indication with number of bytes
       Q_SIGNAL void progressed(qint64 done, qint64 total);
       /// Signals for progress that uses abstract integer values
       Q_SIGNAL void hasProgressMaximum(int total);
       Q_SIGNAL void hasProgressValue(int done);
       ///
       Q_SIGNAL void finished(bool ok, const QString & error);
    };
    
    /// A thread that is always destructible: if quits the event loop and waits
    /// for it to finish.
    class Thread : public QThread {
    public:
       ~Thread() { quit(); wait(); }
    };
    
    int main(int argc, char *argv[])
    {
       QApplication a(argc, argv);
       QString src = QFileDialog::getOpenFileName(0, "Source File");
       if (src.isEmpty()) return 1;
       QString dst = QFileDialog::getSaveFileName(0, "Destination File");
       if (dst.isEmpty()) return 1;
    
       QProgressDialog dlg("File Copy Progress", "Cancel", 0, 100);
       Q_ASSERT(!dlg.isModal());
       Thread thread;
       FileCopier copier;
       copier.moveToThread(&thread);
       thread.start();
       dlg.connect(&copier, SIGNAL(hasProgressMaximum(int)),
                   SLOT(setMaximum(int)));
       dlg.connect(&copier, SIGNAL(hasProgressValue(int)),
                   SLOT(setValue(int)));
       copier.connect(&dlg, SIGNAL(canceled()), SLOT(cancel()));
       a.connect(&copier, SIGNAL(finished(bool,QString)), SLOT(quit()));
       // The copy method is thread safe.
       copier.copy(src, dst);
       return a.exec();
    }
    
    #include "main.moc"