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++.
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!
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.