Como você itera por meio de cada arquivo / diretório recursivamente no C ++ padrão?
Como você itera por meio de cada arquivo / diretório recursivamente no C ++ padrão?
Respostas:
No C ++ padrão, tecnicamente não há como fazer isso, pois o C ++ padrão não tem concepção de diretórios. Se você quiser expandir sua rede um pouco, você pode querer dar uma olhada em Boost.FileSystem . Isso foi aceito para inclusão no TR2, portanto, oferece a melhor chance de manter sua implementação o mais próximo possível do padrão.
Um exemplo, retirado diretamente do site:
bool find_file( const path & dir_path, // in this directory,
const std::string & file_name, // search for this name,
path & path_found ) // placing path here if found
{
if ( !exists( dir_path ) ) return false;
directory_iterator end_itr; // default construction yields past-the-end
for ( directory_iterator itr( dir_path );
itr != end_itr;
++itr )
{
if ( is_directory(itr->status()) )
{
if ( find_file( itr->path(), file_name, path_found ) ) return true;
}
else if ( itr->leaf() == file_name ) // see below
{
path_found = itr->path();
return true;
}
}
return false;
}
Do C ++ 17 em diante, o <filesystem>
cabeçalho e o intervalo for
, você pode simplesmente fazer isso:
#include <filesystem>
using recursive_directory_iterator = std::filesystem::recursive_directory_iterator;
...
for (const auto& dirEntry : recursive_directory_iterator(myPath))
std::cout << dirEntry << std::endl;
A partir do C ++ 17, std::filesystem
faz parte da biblioteca padrão e pode ser encontrado no <filesystem>
cabeçalho (não é mais "experimental").
using
, use em seu namespace
lugar.
Se estiver usando a API Win32, você pode usar as funções FindFirstFile e FindNextFile .
http://msdn.microsoft.com/en-us/library/aa365200(VS.85).aspx
Para travessia recursiva de diretórios, você deve inspecionar cada WIN32_FIND_DATA.dwFileAttributes para verificar se o bit FILE_ATTRIBUTE_DIRECTORY está definido. Se o bit estiver definido, você pode chamar recursivamente a função com esse diretório. Alternativamente, você pode usar uma pilha para fornecer o mesmo efeito de uma chamada recursiva, mas evitando o estouro de pilha para árvores de caminho muito longas.
#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>
using namespace std;
bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA ffd;
wstring spec;
stack<wstring> directories;
directories.push(path);
files.clear();
while (!directories.empty()) {
path = directories.top();
spec = path + L"\\" + mask;
directories.pop();
hFind = FindFirstFile(spec.c_str(), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return false;
}
do {
if (wcscmp(ffd.cFileName, L".") != 0 &&
wcscmp(ffd.cFileName, L"..") != 0) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
directories.push(path + L"\\" + ffd.cFileName);
}
else {
files.push_back(path + L"\\" + ffd.cFileName);
}
}
} while (FindNextFile(hFind, &ffd) != 0);
if (GetLastError() != ERROR_NO_MORE_FILES) {
FindClose(hFind);
return false;
}
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
}
return true;
}
int main(int argc, char* argv[])
{
vector<wstring> files;
if (ListFiles(L"F:\\cvsrepos", L"*", files)) {
for (vector<wstring>::iterator it = files.begin();
it != files.end();
++it) {
wcout << it->c_str() << endl;
}
}
return 0;
}
Você pode torná-lo ainda mais simples com o novo intervalo C ++ 11 baseado for
e Boost :
#include <boost/filesystem.hpp>
using namespace boost::filesystem;
struct recursive_directory_range
{
typedef recursive_directory_iterator iterator;
recursive_directory_range(path p) : p_(p) {}
iterator begin() { return recursive_directory_iterator(p_); }
iterator end() { return recursive_directory_iterator(); }
path p_;
};
for (auto it : recursive_directory_range(dir_path))
{
std::cout << it << std::endl;
}
Uma solução rápida é usar a biblioteca Dirent.h do C.
Fragmento de código de trabalho da Wikipedia:
#include <stdio.h>
#include <dirent.h>
int listdir(const char *path) {
struct dirent *entry;
DIR *dp;
dp = opendir(path);
if (dp == NULL) {
perror("opendir: Path does not exist or could not be read.");
return -1;
}
while ((entry = readdir(dp)))
puts(entry->d_name);
closedir(dp);
return 0;
}
Além do boost :: filesystem mencionado acima, você pode examinar wxWidgets :: wxDir e Qt :: QDir .
Ambos wxWidgets e Qt são frameworks C ++ de plataforma cruzada de código aberto.
wxDir
fornece uma maneira flexível de percorrer arquivos recursivamente usando Traverse()
uma GetAllFiles()
função mais simples . Você também pode implementar a travessia com as funções GetFirst()
e GetNext()
(presumo que Traverse () e GetAllFiles () sejam invólucros que eventualmente usam as funções GetFirst () e GetNext ()).
QDir
fornece acesso a estruturas de diretório e seu conteúdo. Existem várias maneiras de percorrer os diretórios com QDir. Você pode iterar sobre o conteúdo do diretório (incluindo subdiretórios) com QDirIterator que foi instanciado com o sinalizador QDirIterator :: Subdirectories. Outra maneira é usar a função GetEntryList () do QDir e implementar uma travessia recursiva.
Aqui está um código de amostra (retirado daqui # Exemplo 8-5) que mostra como iterar em todos os subdiretórios.
#include <qapplication.h>
#include <qdir.h>
#include <iostream>
int main( int argc, char **argv )
{
QApplication a( argc, argv );
QDir currentDir = QDir::current();
currentDir.setFilter( QDir::Dirs );
QStringList entries = currentDir.entryList();
for( QStringList::ConstIterator entry=entries.begin(); entry!=entries.end(); ++entry)
{
std::cout << *entry << std::endl;
}
return 0;
}
Boost :: filesystem fornece recursive_directory_iterator, que é bastante conveniente para esta tarefa:
#include "boost/filesystem.hpp"
#include <iostream>
using namespace boost::filesystem;
recursive_directory_iterator end;
for (recursive_directory_iterator it("./"); it != end; ++it) {
std::cout << *it << std::endl;
}
Você pode usar ftw(3)
ounftw(3)
percorrer uma hierarquia de sistema de arquivos em C ou C ++ em sistemas POSIX .
nftw()
uso.
Você não. O padrão C ++ não tem conceito de diretórios. Depende da implementação transformar uma string em um identificador de arquivo. O conteúdo dessa string e o que ela mapeia dependem do sistema operacional. Lembre-se de que o C ++ pode ser usado para escrever esse sistema operacional, portanto, ele é usado em um nível em que a pergunta como iterar por meio de um diretório ainda não foi definida (porque você está escrevendo o código de gerenciamento de diretório).
Veja a documentação da API do seu sistema operacional para saber como fazer isso. Se você precisa ser portátil, terá que ter vários #ifdef s para vários sistemas operacionais.
Você provavelmente ficaria melhor com o boost ou com o sistema de arquivos experimental do c ++ 14. SE você estiver analisando um diretório interno (ou seja, usado pelo seu programa para armazenar dados depois que o programa foi fechado), faça um arquivo de índice que tenha um índice do conteúdo do arquivo. A propósito, você provavelmente precisará usar o boost no futuro, então, se não o tiver instalado, instale-o! Em segundo lugar, você pode usar uma compilação condicional, por exemplo:
#ifdef WINDOWS //define WINDOWS in your code to compile for windows
#endif
O código para cada caso é retirado de https://stackoverflow.com/a/67336/7077165
#ifdef POSIX //unix, linux, etc.
#include <stdio.h>
#include <dirent.h>
int listdir(const char *path) {
struct dirent *entry;
DIR *dp;
dp = opendir(path);
if (dp == NULL) {
perror("opendir: Path does not exist or could not be read.");
return -1;
}
while ((entry = readdir(dp)))
puts(entry->d_name);
closedir(dp);
return 0;
}
#endif
#ifdef WINDOWS
#include <windows.h>
#include <string>
#include <vector>
#include <stack>
#include <iostream>
using namespace std;
bool ListFiles(wstring path, wstring mask, vector<wstring>& files) {
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA ffd;
wstring spec;
stack<wstring> directories;
directories.push(path);
files.clear();
while (!directories.empty()) {
path = directories.top();
spec = path + L"\\" + mask;
directories.pop();
hFind = FindFirstFile(spec.c_str(), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return false;
}
do {
if (wcscmp(ffd.cFileName, L".") != 0 &&
wcscmp(ffd.cFileName, L"..") != 0) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
directories.push(path + L"\\" + ffd.cFileName);
}
else {
files.push_back(path + L"\\" + ffd.cFileName);
}
}
} while (FindNextFile(hFind, &ffd) != 0);
if (GetLastError() != ERROR_NO_MORE_FILES) {
FindClose(hFind);
return false;
}
FindClose(hFind);
hFind = INVALID_HANDLE_VALUE;
}
return true;
}
#endif
//so on and so forth.
Você precisa chamar funções específicas do sistema operacional para travessia do sistema de arquivos, como open()
e readdir()
. O padrão C não especifica nenhuma função relacionada ao sistema de arquivos.
Estamos em 2019. Temos a biblioteca padrão do sistema de arquivos em C++
. O Filesystem library
fornece recursos para executar operações em sistemas de arquivos e seus componentes, como caminhos, arquivos regulares e diretórios.
Há uma observação importante neste link se você estiver considerando questões de portabilidade. Diz:
Os recursos da biblioteca do sistema de arquivos podem estar indisponíveis se um sistema de arquivos hierárquico não estiver acessível para a implementação ou se não fornecer os recursos necessários. Alguns recursos podem não estar disponíveis se não forem suportados pelo sistema de arquivos subjacente (por exemplo, o sistema de arquivos FAT não tem links simbólicos e proíbe vários links físicos). Nesses casos, os erros devem ser relatados.
A biblioteca do sistema de arquivos foi originalmente desenvolvida como boost.filesystem
, foi publicada como a especificação técnica ISO / IEC TS 18822: 2015 e, finalmente, foi incorporada ao ISO C ++ a partir do C ++ 17. A implementação boost está disponível atualmente em mais compiladores e plataformas do que a biblioteca C ++ 17.
@ adi-shavit respondeu a esta pergunta quando fazia parte de std :: experimental e ele atualizou esta resposta em 2017. Eu quero dar mais detalhes sobre a biblioteca e mostrar um exemplo mais detalhado.
std :: filesystem :: recursive_directory_iterator é um LegacyInputIterator
que itera sobre os elementos directory_entry de um diretório e, recursivamente, sobre as entradas de todos os subdiretórios. A ordem de iteração não é especificada, exceto que cada entrada do diretório é visitada apenas uma vez.
Se você não quiser iterar recursivamente sobre as entradas de subdiretórios, então o directory_iterator deve ser usado.
Ambos os iteradores retornam um objeto de directory_entry . directory_entry
tem várias funções de membro útil, como is_regular_file
, is_directory
, is_socket
, is_symlink
etc. Os path()
função de membro retorna um objeto do std :: filesystem :: caminho e ele pode ser usado para obter file extension
, filename
, root name
.
Considere o exemplo abaixo. Tenho usado Ubuntu
e compilado no terminal usando
g ++ example.cpp --std = c ++ 17 -lstdc ++ fs -Wall
#include <iostream>
#include <string>
#include <filesystem>
void listFiles(std::string path)
{
for (auto& dirEntry: std::filesystem::recursive_directory_iterator(path)) {
if (!dirEntry.is_regular_file()) {
std::cout << "Directory: " << dirEntry.path() << std::endl;
continue;
}
std::filesystem::path file = dirEntry.path();
std::cout << "Filename: " << file.filename() << " extension: " << file.extension() << std::endl;
}
}
int main()
{
listFiles("./");
return 0;
}
Você não. C ++ padrão não se expõe ao conceito de diretório. Especificamente, não fornece nenhuma maneira de listar todos os arquivos em um diretório.
Um hack horrível seria usar chamadas system () e analisar os resultados. A solução mais razoável seria usar algum tipo de biblioteca de plataforma cruzada, como Qt ou mesmo POSIX .
Você pode usar std::filesystem::recursive_directory_iterator
. Mas cuidado, isso inclui links simbólicos (soft). Se você quiser evitá-los, você pode usar is_symlink
. Exemplo de uso:
size_t directorySize(const std::filesystem::path& directory)
{
size_t size{ 0 };
for (const auto& entry : std::filesystem::recursive_directory_iterator(directory))
{
if (entry.is_regular_file() && !entry.is_symlink())
{
size += entry.file_size();
}
}
return size;
}
Se você estiver no Windows, pode usar FindFirstFile junto com a API FindNextFile. Você pode usar FindFileData.dwFileAttributes para verificar se um determinado caminho é um arquivo ou diretório. Se for um diretório, você pode repetir o algoritmo recursivamente.
Aqui, reuni um código que lista todos os arquivos em uma máquina Windows.
A caminhada na árvore de arquivos ftw
é uma forma recursiva de bloquear toda a árvore de diretórios no caminho. Mais detalhes estão aqui .
NOTA: Você também pode usar o fts
que pode pular arquivos ocultos como .
ou ..
ou.bashrc
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <string.h>
int list(const char *name, const struct stat *status, int type)
{
if (type == FTW_NS)
{
return 0;
}
if (type == FTW_F)
{
printf("0%3o\t%s\n", status->st_mode&0777, name);
}
if (type == FTW_D && strcmp(".", name) != 0)
{
printf("0%3o\t%s/\n", status->st_mode&0777, name);
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc == 1)
{
ftw(".", list, 1);
}
else
{
ftw(argv[1], list, 1);
}
return 0;
}
a saída tem a seguinte aparência:
0755 ./Shivaji/
0644 ./Shivaji/20200516_204454.png
0644 ./Shivaji/20200527_160408.png
0644 ./Shivaji/20200527_160352.png
0644 ./Shivaji/20200520_174754.png
0644 ./Shivaji/20200520_180103.png
0755 ./Saif/
0644 ./Saif/Snapchat-1751229005.jpg
0644 ./Saif/Snapchat-1356123194.jpg
0644 ./Saif/Snapchat-613911286.jpg
0644 ./Saif/Snapchat-107742096.jpg
0755 ./Milind/
0644 ./Milind/IMG_1828.JPG
0644 ./Milind/IMG_1839.JPG
0644 ./Milind/IMG_1825.JPG
0644 ./Milind/IMG_1831.JPG
0644 ./Milind/IMG_1840.JPG
Digamos que se você deseja corresponder a um nome de arquivo (exemplo: pesquisar todos os *.jpg, *.jpeg, *.png
arquivos.) Para uma necessidade específica, use fnmatch
.
#include <ftw.h>
#include <stdio.h>
#include <sys/stat.h>
#include <iostream>
#include <fnmatch.h>
static const char *filters[] = {
"*.jpg", "*.jpeg", "*.png"
};
int list(const char *name, const struct stat *status, int type)
{
if (type == FTW_NS)
{
return 0;
}
if (type == FTW_F)
{
int i;
for (i = 0; i < sizeof(filters) / sizeof(filters[0]); i++) {
/* if the filename matches the filter, */
if (fnmatch(filters[i], name, FNM_CASEFOLD) == 0) {
printf("0%3o\t%s\n", status->st_mode&0777, name);
break;
}
}
}
if (type == FTW_D && strcmp(".", name) != 0)
{
//printf("0%3o\t%s/\n", status->st_mode&0777, name);
}
return 0;
}
int main(int argc, char *argv[])
{
if(argc == 1)
{
ftw(".", list, 1);
}
else
{
ftw(argv[1], list, 1);
}
return 0;
}