todos - ¿Cómo puedo obtener la lista de archivos en un directorio usando C o C++?
guardar y leer datos en un archivo.txt en c++ (24)
¿Cómo puedo determinar la lista de archivos en un directorio dentro de mi código C o C ++?
No tengo permiso para ejecutar el comando ls
y analizar los resultados desde mi programa.
¡El sistema lo llama!
system( "dir /b /s /a-d * > file_names.txt" );
Entonces solo lee el archivo.
EDITAR: Esta respuesta se debe considerar un hack, pero realmente funciona (aunque de una manera específica de la plataforma) si no tiene acceso a soluciones más elegantes.
¿Por qué no usar glob()
?
#include <glob.h>
glob_t glob_result;
glob("/your_directory/*",GLOB_TILDE,NULL,&glob_result);
for(unsigned int i=0; i<glob_result.gl_pathc; ++i){
cout << glob_result.gl_pathv[i] << endl;
}
Aquí hay un código muy simple en C++11
usa boost::filesystem
library para obtener los nombres de los archivos en un directorio (excluyendo los nombres de las carpetas):
#include <string>
#include <iostream>
#include <boost/filesystem.hpp>
using namespace std;
using namespace boost::filesystem;
int main()
{
path p("D:/AnyFolder");
for (auto i = directory_iterator(p); i != directory_iterator(); i++)
{
if (!is_directory(i->path())) //we eliminate directories
{
cout << i->path().filename().string() << endl;
}
else
continue;
}
}
La salida es como
file1.txt
file2.dat
C ++ 17 ahora tiene un std::filesystem::directory_iterator
, que se puede usar como
#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
std::string path = "/path/to/directory";
for (const auto & p : fs::directory_iterator(path))
std::cout << p << std::endl; // "p" is the directory entry. Get the path with "p.path()".
}
Además, std::filesystem::recursive_directory_iterator
puede iterar los subdirectorios.
Creo que, debajo del fragmento se puede usar para listar todos los archivos.
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
static void list_dir(const char *path)
{
struct dirent *entry;
DIR *dir = opendir(path);
if (dir == NULL) {
return;
}
while ((entry = readdir(dir)) != NULL) {
printf("%s/n",entry->d_name);
}
closedir(dir);
}
A continuación se muestra la estructura de la estructura dirt
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* offset to the next dirent */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file */
char d_name[256]; /* filename */
};
Dado que los archivos y subdirectorios de un directorio generalmente se almacenan en una estructura de árbol, una forma intuitiva es usar el algoritmo DFS para atravesar recursivamente cada uno de ellos. Este es un ejemplo del sistema operativo Windows mediante el uso de funciones de archivos básicas en io.h. Puede reemplazar estas funciones en otra plataforma. Lo que quiero expresar es que la idea básica de DFS resuelve perfectamente este problema.
#include<io.h>
#include<iostream.h>
#include<string>
using namespace std;
void TraverseFilesUsingDFS(const string& folder_path){
_finddata_t file_info;
string any_file_pattern = folder_path + "//*";
intptr_t handle = _findfirst(any_file_pattern.c_str(),&file_info);
//If folder_path exsist, using any_file_pattern will find at least two files "." and "..",
//of which "." means current dir and ".." means parent dir
if (handle == -1){
cerr << "folder path not exist: " << folder_path << endl;
exit(-1);
}
//iteratively check each file or sub_directory in current folder
do{
string file_name=file_info.name; //from char array to string
//check whtether it is a sub direcotry or a file
if (file_info.attrib & _A_SUBDIR){
if (file_name != "." && file_name != ".."){
string sub_folder_path = folder_path + "//" + file_name;
TraverseFilesUsingDFS(sub_folder_path);
cout << "a sub_folder path: " << sub_folder_path << endl;
}
}
else
cout << "file name: " << file_name << endl;
} while (_findnext(handle, &file_info) == 0);
//
_findclose(handle);
}
Desafortunadamente, el estándar C ++ no define una forma estándar de trabajar con archivos y carpetas de esta manera.
Dado que no existe una plataforma multiplataforma, la mejor forma multiplataforma es utilizar una biblioteca como el módulo del sistema de archivos boost .
Método de refuerzo de plataforma cruzada:
La siguiente función, dada una ruta de directorio y un nombre de archivo, busca recursivamente en el directorio y sus subdirectorios el nombre del archivo, devolviendo un bool y, si tiene éxito, la ruta del archivo que se encontró.
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;
}
Fuente de la página de impulso mencionada anteriormente.
Para sistemas basados en Unix / Linux:
Puedes usar opendir / readdir / closedir .
El código de muestra que busca en el directorio la entrada `` nombre '''' es:
len = strlen(name);
dirp = opendir(".");
while ((dp = readdir(dirp)) != NULL)
if (dp->d_namlen == len && !strcmp(dp->d_name, name)) {
(void)closedir(dirp);
return FOUND;
}
(void)closedir(dirp);
return NOT_FOUND;
Código fuente de las páginas de manual anteriores.
Para un sistema basado en Windows:
puede usar las funciones Win32 API FindFirstFile / FindClose / FindClose .
El siguiente ejemplo de C ++ le muestra un uso mínimo de FindFirstFile.
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
void _tmain(int argc, TCHAR *argv[])
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
if( argc != 2 )
{
_tprintf(TEXT("Usage: %s [target_file]/n"), argv[0]);
return;
}
_tprintf (TEXT("Target file is %s/n"), argv[1]);
hFind = FindFirstFile(argv[1], &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
printf ("FindFirstFile failed (%d)/n", GetLastError());
return;
}
else
{
_tprintf (TEXT("The first file found is %s/n"),
FindFileData.cFileName);
FindClose(hFind);
}
}
Código fuente de las páginas msdn anteriores.
Echa un vistazo a esta clase que utiliza la api win32. Simplemente cree una instancia proporcionando el nombre de la foldername
de la que desea la lista y luego llame al método getNextFile
para obtener el siguiente filename
de filename
del directorio. Creo que necesita windows.h
y stdio.h
.
class FileGetter{
WIN32_FIND_DATAA found;
HANDLE hfind;
char folderstar[255];
int chk;
public:
FileGetter(char* folder){
sprintf(folderstar,"%s//*.*",folder);
hfind = FindFirstFileA(folderstar,&found);
//skip .
FindNextFileA(hfind,&found);
}
int getNextFile(char* fname){
//skips .. when called for the first time
chk=FindNextFileA(hfind,&found);
if (chk)
strcpy(fname, found.cFileName);
return chk;
}
};
En tareas pequeñas y sencillas que no uso boost, uso dirent.h, que también está disponible para Windows:
DIR *dir;
struct dirent *ent;
if ((dir = opendir ("c://src//")) != NULL) {
/* print all the files and directories within directory */
while ((ent = readdir (dir)) != NULL) {
printf ("%s/n", ent->d_name);
}
closedir (dir);
} else {
/* could not open directory */
perror ("");
return EXIT_FAILURE;
}
Es solo un pequeño archivo de encabezado y hace la mayoría de las cosas simples que necesita sin usar un enfoque basado en una gran plantilla como el impulso (¡no se ofenda, me gusta el impulso!).
El autor de la capa de compatibilidad de Windows es Toni Ronkko. En Unix, es un encabezado estándar.
ACTUALIZACIÓN 2017 :
En C ++ 17 ahora hay una forma oficial de listar los archivos de su sistema de archivos: std::filesystem
. Hay una excelente respuesta de Shreevardhan continuación con este código fuente:
#include <string>
#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main()
{
std::string path = "/path/to/directory";
for (auto & p : fs::directory_iterator(path))
std::cout << p << std::endl;
}
Considere la posibilidad de aumentar su respuesta, si está utilizando el enfoque de C ++ 17.
Espero que este código te ayude.
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
string wchar_t2string(const wchar_t *wchar)
{
string str = "";
int index = 0;
while(wchar[index] != 0)
{
str += (char)wchar[index];
++index;
}
return str;
}
wchar_t *string2wchar_t(const string &str)
{
wchar_t wchar[260];
int index = 0;
while(index < str.size())
{
wchar[index] = (wchar_t)str[index];
++index;
}
wchar[index] = 0;
return wchar;
}
vector<string> listFilesInDirectory(string directoryName)
{
WIN32_FIND_DATA FindFileData;
wchar_t * FileName = string2wchar_t(directoryName);
HANDLE hFind = FindFirstFile(FileName, &FindFileData);
vector<string> listFileNames;
listFileNames.push_back(wchar_t2string(FindFileData.cFileName));
while (FindNextFile(hFind, &FindFileData))
listFileNames.push_back(wchar_t2string(FindFileData.cFileName));
return listFileNames;
}
void main()
{
vector<string> listFiles;
listFiles = listFilesInDirectory("C://*.txt");
for each (string str in listFiles)
cout << str << endl;
}
Esta implementación realiza su propósito, llenando dinámicamente una matriz de cadenas con el contenido del directorio especificado.
int exploreDirectory(const char *dirpath, char ***list, int *numItems) {
struct dirent **direntList;
int i;
errno = 0;
if ((*numItems = scandir(dirpath, &direntList, NULL, alphasort)) == -1)
return errno;
if (!((*list) = malloc(sizeof(char *) * (*numItems)))) {
fprintf(stderr, "Error in list allocation for file list: dirpath=%s./n", dirpath);
exit(EXIT_FAILURE);
}
for (i = 0; i < *numItems; i++) {
(*list)[i] = stringDuplication(direntList[i]->d_name);
}
for (i = 0; i < *numItems; i++) {
free(direntList[i]);
}
free(direntList);
return 0;
}
Esta respuesta debería funcionar para los usuarios de Windows que han tenido problemas para que esto funcione con Visual Studio con cualquiera de las otras respuestas.
Descarga el archivo dirent.h desde la página de github. Pero es mejor usar el archivo Raw dirent.h y seguir mis pasos a continuación (es cómo lo hice funcionar).
Página de Github para dirent.h para Windows: Página de Github para dirent.h
Raw Dirent File: Raw Dirent.h File
Vaya a su proyecto y añada un nuevo elemento ( Ctrl + Shift + A ). Agregue un archivo de encabezado (.h) y asígnele el nombre dirent.h.
Pegue el código del archivo dirent.h sin procesar en su encabezado.
Incluye "dirent.h" en tu código.
Coloque el siguiente método de
void filefinder()
en su código yvoid filefinder()
desde su funciónmain
o edite la función cómo desea usarlo.#include <stdio.h> #include <string.h> #include "dirent.h" string path = "C:/folder"; //Put a valid path here for folder void filefinder() { DIR *directory = opendir(path.c_str()); struct dirent *direntStruct; if (directory != NULL) { while (direntStruct = readdir(directory)) { printf("File Name: %s/n", direntStruct->d_name); //If you are using <stdio.h> //std::cout << direntStruct->d_name << std::endl; //If you are using <iostream> } } closedir(directory); }
Esto funcionó para mí. Escribe un archivo con solo los nombres (sin ruta) de todos los archivos. Luego lee ese archivo txt y lo imprime por ti.
void DisplayFolderContent()
{
system("dir /n /b * > file_names.txt");
char ch;
std::fstream myStream("file_names.txt", std::fstream::in);
while (myStream.get(ch))
{
std::cout << ch;
}
}
Esto funciona para mi Lo siento si no puedo recordar la fuente. Probablemente sea de una página de manual.
#include <ftw.h>
int AnalizeDirectoryElement (const char *fpath,
const struct stat *sb,
int tflag,
struct FTW *ftwbuf) {
if (tflag == FTW_F) {
std::string strFileName(fpath);
DoSomethingWith(strFileName);
}
return 0;
}
void WalkDirectoryTree (const char * pchFileName) {
int nFlags = 0;
if (nftw(pchFileName, AnalizeDirectoryElement, 20, nFlags) == -1) {
perror("nftw");
}
}
int main() {
WalkDirectoryTree("some_dir/");
}
Intenté seguir el ejemplo dado en both answers y podría valer la pena notar que parece que std::filesystem::directory_entry
ha cambiado para no tener una sobrecarga del operador <<
. En lugar de std::cout << p << std::endl;
Tuve que usar lo siguiente para poder compilar y hacer que funcione:
#include <iostream>
#include <filesystem>
#include <string>
namespace fs = std::filesystem;
int main() {
std::string path = "/path/to/directory";
for(const auto& p : fs::directory_iterator(path))
std::cout << p.path() << std::endl;
}
intentar pasar p
por su cuenta a std::cout <<
dio como resultado un error de sobrecarga faltante.
Manual de GNU FTW
Además, a veces es bueno ir directamente a la fuente (juego de palabras intencionado). Puedes aprender mucho mirando las entrañas de algunos de los comandos más comunes en Linux. He creado un espejo simple de los coreutils de GNU en github (para lectura).
https://github.com/homer6/gnu_coreutils/blob/master/src/ls.c
Tal vez esto no se dirija a Windows, pero se pueden tener varios casos de usar variantes de Unix usando estos métodos.
Espero que ayude...
Para una solución solo en C, por favor revise esto. Solo requiere un encabezado extra:
https://github.com/cxong/tinydir
tinydir_dir dir;
tinydir_open(&dir, "/path/to/dir");
while (dir.has_next)
{
tinydir_file file;
tinydir_readfile(&dir, &file);
printf("%s", file.name);
if (file.is_dir)
{
printf("/");
}
printf("/n");
tinydir_next(&dir);
}
tinydir_close(&dir);
Algunas ventajas sobre otras opciones:
- Es portátil: envuelve POSIX dirent y Windows FindFirstFile
- Utiliza
readdir_r
donde esté disponible, lo que significa que (normalmente) es threadsafe - Soporta Windows UTF-16 a través de las mismas macros de
UNICODE
- Es C90 por lo que incluso los compiladores muy antiguos pueden usarlo
Prueba el impulso para el método de la plataforma x
http://www.boost.org/doc/libs/1_38_0/libs/filesystem/doc/index.htm
o simplemente usa tus archivos específicos del sistema operativo.
Recomiendo usar glob
con este envoltorio reutilizable. Genera un vector<string>
correspondiente a las rutas de archivo que se ajustan al patrón global:
#include <glob.h>
#include <vector>
using std::vector;
vector<string> globVector(const string& pattern){
glob_t glob_result;
glob(pattern.c_str(),GLOB_TILDE,NULL,&glob_result);
vector<string> files;
for(unsigned int i=0;i<glob_result.gl_pathc;++i){
files.push_back(string(glob_result.gl_pathv[i]));
}
globfree(&glob_result);
return files;
}
A continuación, se puede llamar con un patrón de comodín normal del sistema, como:
vector<string> files = globVector("./*");
Shreevardhan respuesta funciona muy bien. Pero si desea usarlo en c ++ 14 simplemente haga un cambio en el namespace fs = experimental::filesystem;
es decir,
#include <string>
#include <iostream>
#include <filesystem>
using namespace std;
namespace fs = experimental::filesystem;
int main()
{
string path = "C://splits//";
for (auto & p : fs::directory_iterator(path))
cout << p << endl;
int n;
cin >> n;
}
Solo algo que quiero compartir y gracias por el material de lectura. Juega un poco con la función un poco para entenderla. Puede que te guste. e significaba extensión, p es para ruta y s es para separador de ruta.
Si la ruta se pasa sin separador final, se agregará un separador a la ruta. Para la extensión, si se ingresa una cadena vacía, la función devolverá cualquier archivo que no tenga una extensión en su nombre. Si se ingresó una sola estrella, se devolverán todos los archivos del directorio. Si e la longitud es mayor que 0 pero no es un solo *, entonces se agregará un punto a e si e no hubiera contenido un punto en la posición cero.
Para un valor de retorno. Si se devuelve un mapa de longitud cero, no se encontró nada, pero el directorio estaba abierto. Si el índice 999 está disponible a partir del valor de retorno, pero el tamaño del mapa es solo de 1, significa que hubo un problema al abrir la ruta del directorio.
Tenga en cuenta que, por eficiencia, esta función se puede dividir en 3 funciones más pequeñas. Además, puede crear una función de llamada que detectará a qué función llamará según la entrada. ¿Por qué es eso más eficiente? Dice que si va a agarrar todo lo que es un archivo, al hacer ese método, la subfunción que se creó para capturar todos los archivos solo tomará todos los archivos y no necesita evaluar ninguna otra condición innecesaria cada vez que encuentre un archivo.
Eso también se aplicaría cuando agarres archivos que no tienen una extensión. Una función específica construida para ese propósito solo evaluaría el clima si el objeto encontrado es un archivo y luego si el nombre del archivo tiene un punto o no.
El ahorro puede no ser mucho si solo lee directorios con no tantos archivos. Pero si está leyendo una gran cantidad de directorios o si el directorio tiene cientos de miles de archivos, podría ser un gran ahorro.
#include <stdio.h>
#include <sys/stat.h>
#include <iostream>
#include <dirent.h>
#include <map>
std::map<int, std::string> getFile(std::string p, std::string e = "", unsigned char s = ''/''){
if ( p.size() > 0 ){
if (p.back() != s) p += s;
}
if ( e.size() > 0 ){
if ( e.at(0) != ''.'' && !(e.size() == 1 && e.at(0) == ''*'') ) e = "." + e;
}
DIR *dir;
struct dirent *ent;
struct stat sb;
std::map<int, std::string> r = {{999, "FAILED"}};
std::string temp;
int f = 0;
bool fd;
if ( (dir = opendir(p.c_str())) != NULL ){
r.erase (999);
while ((ent = readdir (dir)) != NULL){
temp = ent->d_name;
fd = temp.find(".") != std::string::npos? true : false;
temp = p + temp;
if (stat(temp.c_str(), &sb) == 0 && S_ISREG(sb.st_mode)){
if ( e.size() == 1 && e.at(0) == ''*'' ){
r[f] = temp;
f++;
} else {
if (e.size() == 0){
if ( fd == false ){
r[f] = temp;
f++;
}
continue;
}
if (e.size() > temp.size()) continue;
if ( temp.substr(temp.size() - e.size()) == e ){
r[f] = temp;
f++;
}
}
}
}
closedir(dir);
return r;
} else {
return r;
}
}
void printMap(auto &m){
for (const auto &p : m) {
std::cout << "m[" << p.first << "] = " << p.second << std::endl;
}
}
int main(){
std::map<int, std::string> k = getFile("./", "");
printMap(k);
return 0;
}
puede obtener todos los archivos directos en su directorio raíz usando std :: experimental :: filesystem :: directory_iterator (). Luego, lea el nombre de estos archivos de ruta.
#include <iostream>
#include <filesystem>
#include <string>
#include <direct.h>
using namespace std;
namespace fs = std::experimental::filesystem;
void ShowListFile(string path)
{
for(auto &p: fs::directory_iterator(path)) /*get directory */
cout<<p.path().filename()<<endl; // get file name
}
int main() {
ShowListFile("C:/Users/dell/Pictures/Camera Roll/");
getchar();
return 0;
}
Una función es suficiente, no necesita usar ninguna biblioteca de terceros (para Windows).
#include <Windows.h>
vector<string> get_all_files_names_within_folder(string folder)
{
vector<string> names;
string search_path = folder + "/*.*";
WIN32_FIND_DATA fd;
HANDLE hFind = ::FindFirstFile(search_path.c_str(), &fd);
if(hFind != INVALID_HANDLE_VALUE) {
do {
// read all (real) files in current folder
// , delete ''!'' read other 2 default folder . and ..
if(! (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {
names.push_back(fd.cFileName);
}
}while(::FindNextFile(hFind, &fd));
::FindClose(hFind);
}
return names;
}
PD: como lo menciona @Sebastian, puede cambiar *.*
*.ext
para obtener solo los archivos EXT (es decir, de un tipo específico) en ese directorio.
char **getKeys(char *data_dir, char* tablename, int *num_keys)
{
char** arr = malloc(MAX_RECORDS_PER_TABLE*sizeof(char*));
int i = 0;
for (;i < MAX_RECORDS_PER_TABLE; i++)
arr[i] = malloc( (MAX_KEY_LEN+1) * sizeof(char) );
char *buf = (char *)malloc( (MAX_KEY_LEN+1)*sizeof(char) );
snprintf(buf, MAX_KEY_LEN+1, "%s/%s", data_dir, tablename);
DIR* tableDir = opendir(buf);
struct dirent* getInfo;
readdir(tableDir); // ignore ''.''
readdir(tableDir); // ignore ''..''
i = 0;
while(1)
{
getInfo = readdir(tableDir);
if (getInfo == 0)
break;
strcpy(arr[i++], getInfo->d_name);
}
*(num_keys) = i;
return arr;
}