Como listar os arquivos de um diretório com C++

Utilizando C++ Moderno de forma simples.


Como listar os arquivos de um diretório com C++


Comumente quando estamos desenvolvendo nossas aplicações precisamos de tarefas corriqueiras para realizar o objetivo final do nosso projeto.

Uma das coisas mais frequentes, dentre outras, é listar arquivos de um diretório e geralmente recorremos a internet, no entanto, é muito simples de entender a lógica e fixarmos esse conceito para diminuirmos a perda de tempo realizando pesquisa.

Nessa dica rápida veremos como fazer isso de forma moderna com C++.


Utilizando o filesystem

A biblioteca filesystem fornece recursos para executar operações em sistemas de arquivos e seus componentes, como caminhos, arquivos regulares e diretórios.

Ela foi originalmente desenvolvida pela Boost e na versão C++ 17 ela foi finalmente adotada pelo comitê para fazer parte da biblioteca padrão(STL).

Para usá-la incluiremos:

#include <filesystem>

O namespace dela é muito grande, logo, é sempre interessante reduzir o caminho simplificando para fs somente, e isso é bom ser feito logo após a inclusão do cabeçalho:

namespace fs = std::filesystem;

Se estiver usando um linter, pode ser que ele acuse o warning: ■ Namespace alias decl 'fs' is unused (fix available), isso é porque declaramos, mas ainda não usamos no nosso código, porém já já usaremos!

Agora vamos criar uma string que armazenará a pasta/diretório que queremos listar os arquivos(ex.: /path/to/directory), para esse exemplos listaremos o diretório atual mesmo:

std::string path = "./";

E para listar os arquivos usaremos um Range-based for loop que também é carinhosamente confundido com for-each e percorremos nossa pasta utilizando um iterador nativo da filesystem que é o directory_iterator() e dentro do loop printaremos o elemento que nada mais é que o(s) arquivos do diretório, usando a função-mebro path():

Lembrando que se houver um subdiretório ele também listará.

for (const auto &entry : fs::directory_iterator(path)){
  std::cout << entry.path() << '\n';
}

Após compilar normalmente e rodar, você notará os arquivos(e/ou diretórios, se houver) sendo listados.

Se quiser ter certeza que os arquivos estão sendo listado um de cada vez, você pode pausar a exibição com um sleep:

std::this_thread::sleep_for(std::chrono::seconds(1)); 

Nesse caso haverá um atraso de 1 segundo na exibição.

O código completo é:

#include <iostream>
#include <filesystem>
//#include <chrono>
//#include <thread>

namespace fs = std::filesystem;

int main(){
  std::string path = "./";

  for (const auto &entry : fs::directory_iterator(path)){
    std::cout << entry.path() << '\n';
    //std::this_thread::sleep_for(std::chrono::seconds(1)); 
  }

  return 0;
}

A pausa está comentada!


Exibindo na ordem

Suponhamos que você tenham diversos arquivos assim: file1.jpg, file2.jpg,...file.10.jpg, ... file31.jpg, ao listar eles não aparecerão em ordem alfabética.

Para resolver isso, além de respeitar a ordem das dezenas, adicione os cabeçalhos:

#include <vector>
#include <algorithm>
#include <cctype> // isdigit
#include <sstream> // std::stringstream

Crie uma função com um lambda interna:

bool num_str_cmp(const fs::path& x, const fs::path& y) {
  auto get_number = [](const fs::path& path) {
    auto str_num = path.stem().string();

    std::string num_part {};
    for (char c : str_num) {
      if (std::isdigit(c)) {
        num_part += c;
      }
    }

    return !num_part.empty() ? std::stoi(num_part) : 0;
  };

  return get_number(x) < get_number(y);
}

E agora ordene o vector com sort:

std::vector<fs::path> files_in_dir;
std::copy(fs::directory_iterator(path), fs::directory_iterator(), std::back_inserter(files_in_dir));

std::sort(files_in_dir.begin(), files_in_dir.end(), num_str_cmp);

for (const auto &entry : files_in_dir){
  std::cout << entry << '\n';
}

Dessa forma resolveremos ambos os problemas: ordem alfabética e respeito das dezenas!

Código completo:

#include <iostream>
#include <filesystem>
#include <vector>
#include <algorithm>
#include <cctype> // isdigit
#include <sstream> // std::stringstream

namespace fs = std::filesystem;

bool num_str_cmp(const fs::path& x, const fs::path& y) {
  auto get_number = [](const fs::path& path) {
    auto str_num = path.stem().string();

    std::string num_part {};
    for (char c : str_num) {
      if (std::isdigit(c)) {
        num_part += c;
      }
    }

    return !num_part.empty() ? std::stoi(num_part) : 0;
  };

  return get_number(x) < get_number(y);
}

int main(){
  std::string path = "./imgs";

  std::vector<fs::path> files_in_dir;
  std::copy(fs::directory_iterator(path), fs::directory_iterator(), std::back_inserter(files_in_dir));

  std::sort(files_in_dir.begin(), files_in_dir.end(), num_str_cmp);

  for (const auto &entry : files_in_dir){
    std::cout << entry << '\n';
  }

  return 0;
}

Para mais informações acesse: https://en.cppreference.com/w/cpp/filesystem.


cpp


Compartilhe


Nosso canal no Youtube

Inscreva-se


Marcos Oliveira

Marcos Oliveira

Desenvolvedor de software
https://github.com/terroo


Crie Aplicativos Gráficos para Linux e Windows com C++

Aprenda C++ Moderno e crie Games, Programas CLI, GUI e TUI de forma fácil.

Saiba Mais

Receba as novidades no seu e-mail!

Após cadastro e confirmação do e-mail, enviaremos semanalmente resumos e também sempre que houver novidades por aqui para que você mantenha-se atualizado!