C++:recursive_directory_Iterator,两个相似的使用,但在其中一个投掷exeption

vmdwslir  于 2023-06-25  发布在  其他
关注(0)|答案(2)|浏览(100)

使用抛出exeption的代码:**terminate在抛出'std::filesystem::__cxx11::filesystem_error'的示例后调用what():文件系统错误:无法递增递归目录迭代器:无效参数

#include <iostream>
#include <filesystem>
#include <string>

using namespace std;
using namespace std::filesystem;

void explore(const directory_entry& directory,size_t& counter) {
  for(const auto& entry : recursive_directory_iterator{ directory.path() }) {
     cout<<"\nREC: "<<entry;
     counter++;
  }
}

int main(int argc,char* argv[]){
    if(argc) cout<<"";
    if(argc!=3){
        cerr<<"Wrong arguments. Usage app \"PATH\" \"three symbols extension\"\n";
        return -1;
    }
    recursive_directory_iterator di{argv[1]},end;
    size_t counter{};
    explore(directory_entry{argv[1]},counter);
    (counter)? cout<<"\nTotal files in "<<argv[1]<<"with extension *"<<argv[2]<<" is "<<counter<<"\n": cout<<"\nFiles with extension "<<argv[2]<<" not found\n";

    

}

不抛出exeption的代码:

#include <filesystem>
#include <iomanip>
#include <iostream>
#include <string>

using namespace std;
using namespace std::filesystem;

struct Attributes {
  size_t size_bytes;
  size_t n_directories;
  size_t n_files;
};

void print_line(const Attributes& attributes, string_view path) {
  cout << setw(12) << attributes.size_bytes << setw(6) << attributes.n_files << setw(6) << attributes.n_directories
       << " " << path << "\n";
}

Attributes explore(const directory_entry& directory) {
  Attributes attributes{};
  for(const auto& entry : recursive_directory_iterator{ directory.path() }) {
     cout<<"\nREC: "<<entry;
  }
  return attributes;
}

int main(int argc, const char** argv) {
  if(argc < 2) {
    cerr << "Usage: treedir PATH";
    return -1;
  }
  path sys_path{};
  std::string args{};
  if(argv[1][0]=='-'){
    args=argv[1];
    sys_path=argv[2];
  }
  else{
    sys_path=argv[1];
  }
  bool recursive{};
  for(auto& val:args){
    if(val=='R') recursive=1;
  }

  cout << "Size         Files  Dirs Name\n";
  cout << "------------ ----- ----- ------------\n";
  Attributes root_attributes{};
  for(const auto& entry : directory_iterator{ sys_path }) {
    try {
      if(entry.is_directory()) {
        if(recursive){
        const auto attributes = explore(entry);
        print_line(attributes, entry.path().string());
      }
        root_attributes.n_directories++;
      } else {
        root_attributes.n_files++;
        error_code ec;
        root_attributes.size_bytes += entry.file_size(ec);
        if(ec)
          cerr << "Error reading file size: " << entry.path().string() << endl;
      }
    } catch(const exception&) {
    }
  }
  print_line(root_attributes, sys_path.string());
}

在第一种情况下,当我从“C:”开始遍历时,我得到了exeption,但是当我使用D:\遍历时,我得到了exeption。在第二种情况下,遍历对于C:\和D:都是可以的。
通过skip_permission_denied应用std::filesystem::directory_options没有帮助。
有什么想法吗

g52tjvyc

g52tjvyc1#

在第二个代码片段中,您通过try {} catch屏蔽了常规类型上的异常,而不采取任何操作。
尝试(对不起)此版本的函数:

void explore(const directory_entry& directory, size_t& counter) {
    try {
        for (const auto& entry : recursive_directory_iterator{ directory.path() }) {
            cout << "\nREC: " << entry.path();
            counter++;
        }
    }
    catch (std::filesystem::filesystem_error e) {
        cerr << e.what();
    }
}

你会看到发生什么事的。在我的情况下

REC: "C:\\$Recycle.Bin"
REC: "C:\\$Recycle.Bin\\S-1-5-18"recursive_directory_iterator::operator++: Access is denied.

程序遇到一个非空的回收站,并拒绝尝试戳它。:P
对于第二种情况,我使用了一个函数,它将在进一步传播异常之前打印诊断消息。注意,使用异常作为控制流可能不是一个好主意。在可能的情况下,最好使用函数返回值。

Attributes explore(const directory_entry& directory, bool verbose) {
    Attributes attributes{};
    try {
        for (const auto& entry : recursive_directory_iterator{ directory.path(),std::filesystem::directory_options::skip_permission_denied }) {
            if (verbose)
                cout << "\nREC: " << entry.path();
        }
    }
    catch (std::filesystem::filesystem_error& e) {
        cerr << directory.path() << ":" << e.what();
        throw e;
    }
    return attributes;
}

并且可以自由添加垃圾邮件控制标志:

bool recursive{}, verbose{};
for (auto& val : args) {
    if (val == 'R') recursive = true;
    if (val == 'v') verbose = true;
}

调用explore():

if (recursive) {
                const auto attributes = explore(entry, verbose);
6tr1vspr

6tr1vspr2#

将你的两个版本简化为基本内容,你正在比较这个:

// A
size_t N = 100;
for (size_t i=0; i<N; ++i) {
     might_throw();
}

// B
size_t N = 100;
for (size_t i=0; i<N; ++i) {
     try {
         might_throw();
     } catch(const exception& e) {
         // do nothing
     }
}

在第一种情况下,当might_throw抛出异常时,异常会在调用堆栈中向上移动,因为在任何地方捕捉到异常,程序都会终止。当在第一个迭代中抛出异常时,其他迭代将不会执行。
在第二个例子中,当might_throw抛出一个异常时,这个异常会被try-catch捕获。不对异常执行任何操作,然后执行下一个迭代。当在第一次迭代中抛出异常时,其他迭代仍然会执行。
你可以用eg std::cout << e.what() << "\n";替换//do nothing,以在出现异常时产生一些输出。很可能您会在代码的两个变体中看到相同的异常被抛出(除了第一个变体在第一个变体之后终止)。

相关问题