使用 C++ 实验时忽略具有其他扩展名的文件的最佳方法 <filesystem>?

The best way to ignore files with other extensions when using the C++ experimental <filesystem>?

对于未来的 C++,是否有比下面的代码片段中显示的更好的方法来忽略具有非所需扩展名的文件?

我正在学习 C++ 实验性 <filesystem> (http://en.cppreference.com/w/cpp/experimental/fs),同时编写一个简单的程序,将文本文件从一个目录转换为另一个目录中的文本文件。该程序通过命令行参数获取输入和输出目录。只应处理具有特定扩展名(如 .csv.txt、...)的文件。输出文件应具有 .xxx 扩展名。

#include <filesystem>
namespace fs = std::tr2::sys; // the implementation from Visual Studio 2015

    ...
    fs::path srcpath{ argv[1] };
    fs::path destpath{ argv[2] };
    ... 
    for (auto name : fs::directory_iterator(srcpath))
    {
        if (!fs::is_regular_file(name))
            continue;                  // ignore the non-files

        fs::path fnameIn{ name };      // input file name

        // Ignore unwanted extensions (here lowered because of Windows).
        string ext{ lower(fnameIn.extension().string()) };
        if (ext != ".txt" && ext != ".csv")
            continue;

        // Build the output filename path.
        fs::path fnameOut{ destpath / fnameIn.filename().replace_extension(".xxx") };

        ... processing ...
    }

基本上,您的问题可以归结为,"given a string, how do I determine if it matches one of a number of possibilities?" 这很简单:将可能性放在 std::set:

//Before loop
std::set<std::string> wanted_exts = {".txt", ".csv"};

//In loop
string ext{ lower(fnameIn.extension().string()) };
if (wanted_exts.find(ext) == wanted_exts.end())
    continue;

当然,您可以随心所欲地保留 wanted_exts,因为它可能不会改变。此外,如果您有 Boost.Containers,我建议将 wanted_exts 设为 flat_set。这将有助于最大限度地减少分配。

std::tr2::sys 是 VS2013 中用于发布文件系统 TS 的命名空间 MSVC,但实际上应该在 std::experimental::v1 命名空间中;为了向后兼容,保留了旧的命名空间。 v1 是一个 inline namespace,因此您可以从名称中删除它并说

namespace fs = std::experimental::filesystem;

假设使用 boost 是一个选项,您可以使用 Boost.Range adaptors. And testing for any one of several extensions can be done using boost::algorithm::any_of_equal.

执行目录条目的过滤
#include <boost/algorithm/cxx11/any_of.hpp>
#include <boost/range/adaptors.hpp>

for(auto const& p : 
      boost::make_iterator_range(fs::directory_iterator(srcpath), {})
      | boost::adaptors::transformed([](auto const& d) { 
          return fs::path(d); })
      | boost::adaptors::filtered([](auto const& p) { 
          return fs::is_regular_file(p); })
      | boost::adaptors::filtered([](auto const& p) { 
          auto const& exts = { ".txt", ".csv" };
          return boost::algorithm::any_of_equal(exts, p.extension().string()); })
   ) {
    // all filenames here will have one of the extensions you tested for
}

我最终选择的循环的解决方案...

#include <filesystem>
namespace fs = std::experimental::filesystem;

...

set<string> extensions{ ".txt", ".csv" };

for (auto const& name : fs::directory_iterator(srcpath))
{
    if (!fs::is_regular_file(name))
        continue;

    fs::path fnameIn{ name };
    string ext{ lower(fnameIn.extension().string()) };
    if (extensions.find(ext) != extensions.end())
    {
        fs::path fnameOut{ destpath / fnameIn.filename().replace_extension(".xxx") };
        processing(fnameIn, fnameOut);
    }
}