2016-01-29 2 views
-2

С будущим C++ существует ли лучший способ игнорировать файлы с запросами, отличными от требуемых, чем те, которые показаны в нижеприведенном фрагменте кода?Лучший способ игнорировать файлы с другими расширениями при использовании эксперимента C++ <filesystem>?

Я изучаю экспериментатор 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 ... 
    } 
+3

не в C++ 17. Он находится в файловой системе TS. –

+2

Выглядит неплохо для меня. –

+1

Это похоже на то, как это можно было бы сделать. Вы могли бы сделать это (возможно) немного лучше с чем-то вроде [boost :: filter_iterator] (http://www.boost.org/doc/libs/release/libs/iterator/doc/filter_iterator.html). Я хотел бы, чтобы кто-то реализовал [glob] (http://man7.org/linux/man-pages/man3/glob.3.html), используя стандартную библиотеку. – user2079303

ответ

1

В целом, ваш вопрос сводится к «заданной строке», как определить, соответствует ли она одному из нескольких возможностей? » Это довольно тривиально: положить возможности в 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 a flat_set. Это поможет минимизировать выделение.

+0

Набор строк, действительно! – pepr

+0

Я принял это, поскольку это было ядром вопроса. Вопрос оптимизации будет состоять в том, является ли 'set ' наиболее подходящим контейнером для нескольких расширений. _ «Если у вас есть сомнения, измерьте». В любом случае, будучи разработчиком игры и, возможно, ощущая потребление памяти в наборе строк по сравнению с другим контейнером, вы бы все равно выбрали набор строк? Спасибо;) – pepr

+0

@pepr: Я уже предложил один: 'flat_set'. –

1

std::tr2::sys было пространством имен MSVC, используемым в VS2013, для отправки файловой системы TS, но это фактически должно быть в пространстве имен std::experimental::v1; старое пространство имен было сохранено для обратной совместимости. v1 является inline namespace, так что вы можете упасть, что от имени и сказать

namespace fs = std::experimental::filesystem; 

Предполагая, используя подталкивание вариант, можно выполнить фильтрацию записей каталога с помощью Boost.Range adaptors. И тестирование для любого из нескольких расширений может быть выполнено с использованием 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 
} 
+0

Спасибо за пример boost (+1) и за лучшее имя пространства имен, о котором я не знал. Код интересен. Я должен обернуть голову. Вопрос в том, насколько он читается как не-лямбда-код (по крайней мере для меня). – pepr

0

Решение петли, что я, наконец, выбрали ...

#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); 
    } 
} 
+0

'extensions.find (ext)! = Extensions.end()' true, если расширение было * найдено *. Это означает, что он находится в комплекте. И вы его обработаете. Это противоположность * игнорирования. –

+0

Да. Это желаемое поведение. Ранний подход использовал обратное условие плюс пропуск через 'continue'. Собственно, заголовок вводит в заблуждение. Я исправлю это. – pepr