C++: recursive_directory_iterator。两者相似,但其中一个会抛出异常。

huangapple go评论72阅读模式
英文:

C++: recursive_directory_Iterator. two similar using,but in one of them throwing exeption

问题

抛出异常的代码部分:**在抛出std::filesystem::__cxx11::filesystem_error实例后终止,错误信息如下:
filesystem error: 无法增加递归目录迭代器: 无效参数

#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 << "参数错误。用法:app \"路径\" \"三个字符的扩展名\"\n";
    return -1;
  }
  recursive_directory_iterator di{ argv[1] }, end;
  size_t counter{};
  explore(directory_entry{ argv[1] }, counter);
  (counter) ? cout << "\n总共找到扩展名为 *" << argv[2] << " 的文件数为 " << counter << "\n" : cout << "\n未找到扩展名为 " << argv[2] << " 的文件\n";
}

没有抛出异常的代码部分:

#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 << "用法: treedir 路径";
    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 << "读取文件大小出错: " << entry.path().string() << endl;
      }
    }
    catch (const exception&) {
    }
  }
  print_line(root_attributes, sys_path.string());
}

请注意,这些代码的翻译可能会有一些细微的错误,因为源代码中包含一些代码注释和特殊字符,但基本上已经被翻译成中文。如果您需要更多详细信息或有其他问题,请随时提出。

英文:

Code with throwing exeption: **terminate called after throwing an instance of 'std::filesystem::__cxx11::filesystem_error'
what(): filesystem error: cannot increment recursive directory iterator: Invalid argument

#include &lt;iostream&gt;
#include &lt;filesystem&gt;
#include &lt;string&gt;
using namespace std;
using namespace std::filesystem;
void explore(const directory_entry&amp; directory,size_t&amp; counter) {
for(const auto&amp; entry : recursive_directory_iterator{ directory.path() }) {
cout&lt;&lt;&quot;\nREC: &quot;&lt;&lt;entry;
counter++;
}
}
int main(int argc,char* argv[]){
if(argc) cout&lt;&lt;&quot;&quot;;
if(argc!=3){
cerr&lt;&lt;&quot;Wrong arguments. Usage app \&quot;PATH\&quot; \&quot;three symbols extension\&quot;\n&quot;;
return -1;
}
recursive_directory_iterator di{argv[1]},end;
size_t counter{};
explore(directory_entry{argv[1]},counter);
(counter)? cout&lt;&lt;&quot;\nTotal files in &quot;&lt;&lt;argv[1]&lt;&lt;&quot;with extension *&quot;&lt;&lt;argv[2]&lt;&lt;&quot; is &quot;&lt;&lt;counter&lt;&lt;&quot;\n&quot;: cout&lt;&lt;&quot;\nFiles with extension &quot;&lt;&lt;argv[2]&lt;&lt;&quot; not found\n&quot;;
}

Code with no exeption throwing:

#include &lt;filesystem&gt;
#include &lt;iomanip&gt;
#include &lt;iostream&gt;
#include &lt;string&gt;
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&amp; attributes, string_view path) {
cout &lt;&lt; setw(12) &lt;&lt; attributes.size_bytes &lt;&lt; setw(6) &lt;&lt; attributes.n_files &lt;&lt; setw(6) &lt;&lt; attributes.n_directories
&lt;&lt; &quot; &quot; &lt;&lt; path &lt;&lt; &quot;\n&quot;;
}
Attributes explore(const directory_entry&amp; directory) {
Attributes attributes{};
for(const auto&amp; entry : recursive_directory_iterator{ directory.path() }) {
cout&lt;&lt;&quot;\nREC: &quot;&lt;&lt;entry;
}
return attributes;
}
int main(int argc, const char** argv) {
if(argc &lt; 2) {
cerr &lt;&lt; &quot;Usage: treedir PATH&quot;;
return -1;
}
path sys_path{};
std::string args{};
if(argv[1][0]==&#39;-&#39;){
args=argv[1];
sys_path=argv[2];
}
else{
sys_path=argv[1];
}
bool recursive{};
for(auto&amp; val:args){
if(val==&#39;R&#39;) recursive=1;
}
cout &lt;&lt; &quot;Size         Files  Dirs Name\n&quot;;
cout &lt;&lt; &quot;------------ ----- ----- ------------\n&quot;;
Attributes root_attributes{};
for(const auto&amp; 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 &lt;&lt; &quot;Error reading file size: &quot; &lt;&lt; entry.path().string() &lt;&lt; endl;
}
} catch(const exception&amp;) {
}
}
print_line(root_attributes, sys_path.string());
}

In first case, when im start traverse from "C:&quot; im getting exeption,but when i use D:\ traversing ok.In second case,traversing is ok for both C:\ and D:.

applying std::filesystem::directory_options by skip_permission_denied is no help.

Any ideas?

答案1

得分: 2

在第二段代码中,你使用 try {} catch 来捕获一个通用类型的异常,但没有采取任何操作。

尝试(抱歉)使用以下版本的函数:

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

你会看到发生了什么。

在我的情况下:

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

程序遇到了一个非空的回收站,并被拒绝了尝试访问的操作。 C++: recursive_directory_iterator。两者相似,但其中一个会抛出异常。

对于第二种情况,我使用了一个函数,在将异常传播到更高层之前打印诊断消息。请注意,将异常用于控制流可能不是一个好主意。在可能的情况下,最好使用函数返回值。

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

我还有自由添加了一个垃圾邮件控制标志:

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

调用 explore()

if (recursive) {
    const auto attributes = explore(entry, verbose);
}
英文:

In second snippet you mask exceptions by try {} catch on a general type with no action taken.

Try (sorry) this version of function:

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

You'll see what's going on.
In my case

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

Program encountered a non-empty recycling bin and was denied the attempt to poke it. C++: recursive_directory_iterator。两者相似,但其中一个会抛出异常。

For second case I used a function which would print a diagnostic message before propagating exception further up. Note, it might be not a good idea to use exceptions as a control flow. Where possible, it's prefereable to use function return values.

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

and had liberty to add spam-control flag:

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

To call explore():

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

答案2

得分: 0

将您的两个版本简化后,您正在比较以下内容:

// 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) {
// 什么都不做
}
}

在第一个版本中,当might_throw抛出异常时,异常会沿着调用堆栈向上传递,因为您没有在任何地方捕获异常,所以程序将终止。当在第一次迭代中抛出异常时,其他迭代将不会执行。

在第二个版本中,当might_throw抛出异常时,异常会被try-catch捕获。您没有对异常做任何处理,然后下一次迭代会执行。当在第一次迭代中抛出异常时,其他迭代仍然会执行。

您可以将//do nothing替换为例如std::cout << e.what() << "\n";,以在发生异常时生成一些输出。最有可能的情况是,在您的代码的两个变体中都会看到相同的异常被抛出(除了第一个版本在第一次迭代后会终止)。

英文:

Reducing your two versions to the essentials, you are comparing this:

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

with

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

In the first, when might_throw throws an exception then the exception travels up in the call stack and because nowhere you catch the exception the program will terminate. When an exception is thrown in the first iterations, then other iterations will not be executed.

In the second, when might_throw throws an exception then the exception is caught by the try-catch. You don't do anything with the exception and then the next iteration is executed. When an exception is thrown in the first iteration, then the other iterations are still executed.

You can replace //do nothing with eg std::cout &lt;&lt; e.what() &lt;&lt; &quot;\n&quot;; to produce some output in case of an exception. Most likely you will see the same exceptions being thrown in both variants of your code (except that the first will terminate after the first).

huangapple
  • 本文由 发表于 2023年6月19日 17:24:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76505289.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定