【问题标题】:Copy directory using Qt使用 Qt 复制目录
【发布时间】:2011-02-01 22:51:20
【问题描述】:

我想将一个目录从一个驱动器复制到另一个驱动器。我选择的目录包含许多子目录和文件。

如何使用 Qt 实现相同的功能?

【问题讨论】:

    标签: qt qt4 directory-traversal


    【解决方案1】:

    艰难的道路。单独复制每个文件。

    • 使用QDir::entryList() 遍历目录的内容
    • 使用QDir::cd()QDir::cdUp()进出目录
    • 使用QDir::mkdir()QDir::mkpath() 创建新的文件夹树
    • 最后,使用QFile::copy() 复制实际文件

    【讨论】:

      【解决方案2】:

      您可以手动执行以下操作:

      1)。使用下面的 func 生成文件夹/文件列表(递归) - 目标文件。

      static void recurseAddDir(QDir d, QStringList & list) {
      
          QStringList qsl = d.entryList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files);
      
          foreach (QString file, qsl) {
      
              QFileInfo finfo(QString("%1/%2").arg(d.path()).arg(file));
      
              if (finfo.isSymLink())
                  return;
      
              if (finfo.isDir()) {
      
                  QString dirname = finfo.fileName();
                  QDir sd(finfo.filePath());
      
                  recurseAddDir(sd, list);
      
              } else
                  list << QDir::toNativeSeparators(finfo.filePath());
          }
      }
      

      2)。然后您可以开始将文件从目标 list 复制到新的源目录,如下所示:

      for (int i = 0; i < gtdStringList.count(); i++) {
      
          progressDialog.setValue(i);
          progressDialog.setLabelText(tr("%1 Coping file number %2 of %3 ")
              .arg((conf->isConsole) ? tr("Making copy of the Alta-GTD\n") : "")
              .arg(i + 1)
              .arg(gtdStringList.count()));
      
          qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
      
          if (progressDialog.wasCanceled()) {
      
              // removing tmp files/folders
              rmDirectoryRecursive(tmpFolder);
              rmDirectoryRecursive(tmpFolderPlus);
              setEnableGUI(true);
              return;
          }
      
          // coping
          if (!QFile::copy(gtdStringList.at(i), tmpStringList.at(i))) {
      
              if (warningFlag) {
      
                  QMessageBox box(this);
                  QString name = tr("Question");
                  QString file1 = getShortName(gtdStringList.at(i), QString("\\...\\"));
                  QString file2 = getShortName(tmpStringList.at(i), QString("\\...\\"));
                  QString text = tr("Cannot copy <b>%1</b> <p>to <b>%2</b>"   \
                     "<p>This file will be ignored, just press <b>Yes</b> button" \
                     "<p>Press <b>YesToAll</b> button to ignore other warnings automatically..."  \
                     "<p>Or press <b>Abort</b> to cancel operation").arg(file1).arg(file2);
      
                  box.setModal(true);
                  box.setWindowTitle(name);
                  box.setText(QString::fromLatin1("%1").arg(text));
                  box.setIcon(QMessageBox::Question);
                  box.setStandardButtons(QMessageBox::YesToAll | QMessageBox::Yes | QMessageBox::Abort);
      
                  switch (box.exec()) {                   
                      case (QMessageBox::YesToAll):
                          warningFlag = false;
                          break;
                      case (QMessageBox::Yes):
                          break;
                      case (QMessageBox::Abort):
                          rmDirectoryRecursive(tmpFolder);
                          rmDirectoryRecursive(tmpFolderPlus);
                          setEnableGUI(true);
                          return;
                  }
              }
          }
      }
      

      仅此而已。祝你好运!

      【讨论】:

      • 我已尝试将我的更正作为编辑发布,但似乎没有人理解它,所以我将其发布在这里。使用QString("%1/%2").arg(d.path()).arg(file) 通常不是一个好主意,因为可以在文件或路径名中找到(在大多数文件系统中)“%1”或“%2”。以cygwin创建的这条路径为例c:\cyg\ftp%3a%2f%2fcygwin.mirrorcatalogs.com%2fcygwin%2f.
      • 可以说这是d.path()file 中的内容text.txt。 '%1' 将替换为 d.path() 以形成 c:\cyg\ftp%3a%2f%2fcygwin.mirrorcatalogs.com%2fcygwin%2f/%2。最后你会得到c:\cyg\ftp%3atext.txtftext.txtfcygwin.mirrorcatalogs.comtext.txtfcygwintext.txtf/text.txt。更好的选择是d.path().append('/').append(file)
      • 如果你使用 append 它返回一个引用,如果你在其他地方再次使用它可能会改变。如果被拒绝,请小心
      • 安全的替代方案是QString("%1/%2").arg(d.path(), file) - 但最好使用d.filePath(file) 编写路径名,这将使用平台的目录分隔符。当然,您仍然需要 QString::arg(QString, QString) 来编写消息框文本。
      【解决方案3】:

      我想要类似的东西,并且正在谷歌搜索(徒劳无功),所以这是我必须要做的:

      static bool cpDir(const QString &srcPath, const QString &dstPath)
      {
          rmDir(dstPath);
          QDir parentDstDir(QFileInfo(dstPath).path());
          if (!parentDstDir.mkdir(QFileInfo(dstPath).fileName()))
              return false;
      
          QDir srcDir(srcPath);
          foreach(const QFileInfo &info, srcDir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) {
              QString srcItemPath = srcPath + "/" + info.fileName();
              QString dstItemPath = dstPath + "/" + info.fileName();
              if (info.isDir()) {
                  if (!cpDir(srcItemPath, dstItemPath)) {
                      return false;
                  }
              } else if (info.isFile()) {
                  if (!QFile::copy(srcItemPath, dstItemPath)) {
                      return false;
                  }
              } else {
                  qDebug() << "Unhandled item" << info.filePath() << "in cpDir";
              }
          }
          return true;
      }
      

      它使用了一个看起来非常相似的rmDir 函数:

      static bool rmDir(const QString &dirPath)
      {
          QDir dir(dirPath);
          if (!dir.exists())
              return true;
          foreach(const QFileInfo &info, dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)) {
              if (info.isDir()) {
                  if (!rmDir(info.filePath()))
                      return false;
              } else {
                  if (!dir.remove(info.fileName()))
                      return false;
              }
          }
          QDir parentDir(QFileInfo(dirPath).path());
          return parentDir.rmdir(QFileInfo(dirPath).fileName());
      }
      

      顺便说一句,这不处理链接和特殊文件。

      【讨论】:

        【解决方案4】:
        void copyPath(QString src, QString dst)
        {
            QDir dir(src);
            if (! dir.exists())
                return;
        
            foreach (QString d, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
                QString dst_path = dst + QDir::separator() + d;
                dir.mkpath(dst_path);
                copyPath(src+ QDir::separator() + d, dst_path);
            }
        
            foreach (QString f, dir.entryList(QDir::Files)) {
                QFile::copy(src + QDir::separator() + f, dst + QDir::separator() + f);
            }
        }
        

        【讨论】:

        • 我不知道是我的装备,还是Qt 5.6中的这段代码,但这段代码似乎挂了。它正在进行目录复制,但从未完成。很奇怪。
        • 嗯,是的。第一个副本操作有效,然后后续副本将变得很糟糕。创建一个目录,然后将该目录复制到子目录中。第一个作品,第二个,砰,死了。
        【解决方案5】:

        这基本上是petch's answer,由于它在 Qt 5.6 中对我造成了轻微的变化(这是最热门的问题),所以所有的功劳都归于petch

        功能

        bool copyPath(QString sourceDir, QString destinationDir, bool overWriteDirectory)
        {
            QDir originDirectory(sourceDir);
        
            if (! originDirectory.exists())
            {
                return false;
            }
        
            QDir destinationDirectory(destinationDir);
        
            if(destinationDirectory.exists() && !overWriteDirectory)
            {
                return false;
            }
            else if(destinationDirectory.exists() && overWriteDirectory)
            {
                destinationDirectory.removeRecursively();
            }
        
            originDirectory.mkpath(destinationDir);
        
            foreach (QString directoryName, originDirectory.entryList(QDir::Dirs | \
                                                                      QDir::NoDotAndDotDot))
            {
                QString destinationPath = destinationDir + "/" + directoryName;
                originDirectory.mkpath(destinationPath);
                copyPath(sourceDir + "/" + directoryName, destinationPath, overWriteDirectory);
            }
        
            foreach (QString fileName, originDirectory.entryList(QDir::Files))
            {
                QFile::copy(sourceDir + "/" + fileName, destinationDir + "/" + fileName);
            }
        
            /*! Possible race-condition mitigation? */
            QDir finalDestination(destinationDir);
            finalDestination.refresh();
        
            if(finalDestination.exists())
            {
                return true;
            }
        
            return false;
        }
        

        用途:

        /*! Overwrite existing directories. */
        bool directoryCopied = copyPath(sourceDirectory, destinationDirectory, true);
        
        /*! Do not overwrite existing directories. */
        bool directoryCopied = copyPath(sourceDirectory, destinationDirectory, false);
        

        【讨论】:

        • 有什么不同,为什么?
        【解决方案6】:

        试试这个:

        bool copyDirectoryFiles(const QString &fromDir, const QString &toDir, bool coverFileIfExist)
        {
            QDir sourceDir(fromDir);
            QDir targetDir(toDir);
            if(!targetDir.exists()){    /* if directory don't exists, build it */
                if(!targetDir.mkdir(targetDir.absolutePath()))
                    return false;
            }
        
            QFileInfoList fileInfoList = sourceDir.entryInfoList();
            foreach(QFileInfo fileInfo, fileInfoList){
                if(fileInfo.fileName() == "." || fileInfo.fileName() == "..")
                    continue;
        
                if(fileInfo.isDir()){    /* if it is directory, copy recursively*/
                    if(!copyDirectoryFiles(fileInfo.filePath(),  
                        targetDir.filePath(fileInfo.fileName()), 
                        coverFileIfExist)) 
                        return false;
                }
                else{            /* if coverFileIfExist == true, remove old file first */
                    if(coverFileIfExist && targetDir.exists(fileInfo.fileName())){
                        targetDir.remove(fileInfo.fileName());
                    }
        
                    // files copy
                    if(!QFile::copy(fileInfo.filePath(),  
                        targetDir.filePath(fileInfo.fileName()))){ 
                            return false;
                    }
                }
            }
            return true;
        }
        

        【讨论】:

        • 感谢您提供此代码 sn-p,它可能会提供一些即时帮助。一个正确的解释would greatly improve 其教育价值通过展示为什么这是一个很好的解决问题的方法,并将使它对未来有类似但不相同的问题的读者更有用。请edit您的答案添加解释,并说明适用的限制和假设。
        【解决方案7】:

        我创建了一个库来通过 shell 命令样式 API 操作文件。它支持文件的递归副本并处理更多条件。

        https://github.com/benlau/qtshell#cp

        例子

        cp("-a", ":/*", "/target"); // copy all files from qrc resource to target path recursively
        
        cp("tmp.txt", "/tmp");
        
        cp("*.txt", "/tmp");
        
        cp("/tmp/123.txt", "456.txt");
        
        cp("-va","src/*", "/tmp");
        
        cp("-a", ":resource","/target");
        

        【讨论】:

          【解决方案8】:

          由于我在 macOS 上使用 App-Bundles 时遇到了一些问题,所以这里有一个使用 QDirIterator 的解决方案

          void copyAndReplaceFolderContents(const QString &fromDir, const QString &toDir, bool copyAndRemove = false) {
          QDirIterator it(fromDir, QDirIterator::Subdirectories);
          QDir dir(fromDir);
          const int absSourcePathLength = dir.absoluteFilePath(fromDir).length();
          
          while (it.hasNext()){
              it.next();
              const auto fileInfo = it.fileInfo();
              if(!fileInfo.isHidden()) { //filters dot and dotdot
                  const QString subPathStructure = fileInfo.absoluteFilePath().mid(absSourcePathLength);
                  const QString constructedAbsolutePath = toDir + subPathStructure;
                  
                  if(fileInfo.isDir()){
                      //Create directory in target folder
                      dir.mkpath(constructedAbsolutePath);
                  } else if(fileInfo.isFile()) {
                      //Copy File to target directory
          
                      //Remove file at target location, if it exists. Otherwise QFile::copy will fail
                      QFile::remove(constructedAbsolutePath);
                      QFile::copy(fileInfo.absoluteFilePath(), constructedAbsolutePath);
                  }
              }
          }
          
          if(copyAndRemove)
              dir.removeRecursively();
          

          }

          【讨论】:

            【解决方案9】:

            如果您在基于 linux 的系统上并且 cp 命令存在并且可以运行,那么您可以使用 QProcess 来启动 bash:

            auto copy = new QProcess(this);
            copy->start(QStringLiteral("cp -rv %1 %2").arg(sourceFolder, destinationFolder));
            copy->waitForFinished();
            copy->close();
            

            cp详情:

            • -r 表示递归
            • -v 表示打印成功复制的文件

            注意:如果复制操作较长,则需要管理UI冻结,如here

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2013-10-04
              • 1970-01-01
              • 2021-11-13
              • 1970-01-01
              • 2012-06-16
              相关资源
              最近更新 更多