OpenClassrooms

Parcourir les dossiers avec dirent.h

OpenClassrooms
En Ligne

Prix à consulter
Ou préférez-vous appeler directement le centre?
18088... Voir plus

Infos importantes

Typologie Formation
Méthodologie En ligne
  • Formation
  • En ligne
Description

Grâce à cette formation vous pourrez acquérir les connaissances nécessaires qui vous permettrons d’ajouter des compétences à votre profil et obtenir de solides aptitude qui vous offriront de nombreuses opportunités professionnelles.

Programme

Introduction du cours

Bonjour à tous ! :)

Sur la plupart des forums, on peut voir des Zér0s se poser ce genre de question : « Comment parcourir les dossiers en C ? », « Comment lister les fichiers qui se trouvent dedans ? », etc.

Ce mini-tuto est pour eux ! Grâce au fichier d'en-tête dirent.h, nous allons pouvoir faire ce genre de chose, et plus encore !

Les bases du C sont nécessaires pour suivre et comprendre ce tutoriel. Si vous pensez ne pas les posséder, lisez le tutoriel officiel du site.

N.B. : Si vous êtes à l'aise concernant la manipulation des fichiers, alors ce tutoriel ne devrait a priori vous poser aucun problème. ;)

Ouverture et fermeture de dossiersLe point sur dirent.h

Pour commencer, qu'est-ce que dirent.h ? Comme le .h pouvait le laisser supposer, c'est un header, et il va falloir l'inclure dans nos programmes. ;)
Ce header est portable, il est donc disponible sur Windows, Mac et Linux. D'ailleurs, vous pouvez aller voir dans le dossier include de votre compilateur, il s'y trouve bel et bien. :p
Mais si j'ai mis le mot « portable » en italique, c'est parce qu'il ne l'est pas totalement. dirent.h est un header sous la norme POSIX... Ce qui signifie que c'est un header standard pour les systèmes UNIX. Les codes et les possibilités seront exactement les mêmes pour Linux et Mac par exemple.

Mais... et pour Windows alors ?

dirent.h est bien disponible sous Windows, puisqu'il a été adapté sur cet OS. ^^ Néanmoins, certaines petites choses sont différentes, mais dans les grandes lignes, rien ne change énormément, ne vous inquiétez pas. :p

Les directives de préprocesseur

Je vois que vous en avez déjà marre du blabla. Bien. Passons donc au code. :)
Nous allons avoir besoin de quelques headers à inclure :

#include <stdlib.h> #include <stdio.h> #include <dirent.h> #ifndef WIN32 #include <sys/types.h> #endif

J'ai décidé d'inclure stdio et stdlib pour pouvoir utiliser quelques fonctions standards. Vient ensuite notre fameux dirent.

Qu'est-ce que c'est sys/types ?

Cela fait partie des changements dont j'ai parlé précédemment. Ces lignes de codes signifient : « Si on n'utilise pas un système d'exploitation Windows, alors on inclut sys/types.h ».
Ce header contient des types « inventés » qui ne seront pas forcément indispensables au long de ce chapitre, mais qui se révèleront utiles. Sous Windows (ne me demandez pas pourquoi), on n'a pas besoin d'utiliser ces types. ;)

Ouvrir un répertoire

Vous allez voir, les prototypes des fonctions d'ouverture et de fermeture de dossier ne vont pas vous dépayser des fichiers, la manière de procéder est grosso modo la même. :p

DIR* opendir(const char* chemin) ;

opendir() attend une chaîne de caractères et renvoie un pointeur sur FILE DIR.

Cékoiça DIR ? Et quel chemin faut-il mettre ?

Je vais y aller pas à pas.
DIR est une structure qui simule le dossier que l'on a ouvert. D'ailleurs, petite question... Que signifie DIR ? Directory ! Et que signifie directory ? ... C'est la traduction anglaise de répertoire !
Ensuite, le chemin à mettre, ça, vous avez appris à le faire dans le tutoriel officiel sur la manipulation des fichiers ! Si vous avez un petit trou de mémoire, c'est par ici. N'oubliez pas d'adapter le chemin pour chaque OS ("C:\" pour Windows, "/" pour UNIX).

Si le dossier n'a pas pu être ouvert, opendir() renverra NULL.

Fermer un répertoireint closedir(DIR* repertoire);

On dirait fclose pas vrai ? ^^
Cette fonction attend un pointeur sur DIR et renvoie un int qui correspond à un code d'erreur, 0 si la fermeture s'est bien exécutée, -1 s'il y a eu un souci.

Le code complet commenté#include <stdlib.h> #include <stdio.h> /* stdlib pour exit(), et stdio pour puts() */ #include <dirent.h> /* Pour l'utilisation des dossiers */ #ifndef WIN32 #include <sys/types.h> #endif int main(void) { DIR* rep = NULL; rep = opendir("D:\\Mes Documents\\JaKy"); /* Ouverture d'un dossier */ if (rep == NULL) /* Si le dossier n'a pas pu être ouvert */ exit(1); /* (mauvais chemin par exemple) */ puts("Le dossier a ete ouvert avec succes"); if (closedir(rep) == -1) /* S'il y a eu un souci avec la fermeture */ exit(-1); puts("Le dossier a ete ferme avec succes"); return 0; }

Je pense que ce code est suffisamment explicite pour ne pas être expliqué. ^^

Utiliser errno

errno ? Drôle de nom... Cela a-t-il un rapport direct avec la manipulation des dossiers ?

Non en effet, les deux ne sont pas directement liés. Néanmoins, j'ai décidé d'en toucher quelques mots pour deux raisons :

  • si j'en parle, c'est qu'il y a quand même un rapport avec dirent :) ;

  • j'ai l'impression que l'utilisation d'errno est très méconnue sur le SdZ... Elle est pourtant très utile, alors pourquoi ne pas vous expliquer son fonctionnement ?

La variable errno

Entrons dans le vif du sujet. Je pose le problème : vous avez essayé d'ouvrir un dossier mais pour une raison obscure, l'ouverture a échoué. Comment faire pour savoir d'où vient le problème ? Est-ce tout simplement parce que le dossier n'existe pas, ou peut-être est-il protégé ?
errno est là pour tout nous éclaircir. ;)
Cette variable (globale) de type int peut prendre plusieurs valeurs de defines prédéfinies. Si vous regardez ce lien, vous pouvez voir une longue liste de defines ayant chacun une valeur (valeur que nous ne connaissons pas, et d'ailleurs on s'en moque bien :p ).
Quand une erreur est déclarée, errno prend la valeur d'une de ces valeurs pour permettre au programmeur de connaître la source du problème.

Comment utiliser errno ?

C'est une simple variable, vous n'avez qu'à faire un test d'égalité pour connaître sa valeur :

if (errno == EACCES) /* Accès interdit */ puts("Acces interdit");

N'oubliez pas d'inclure errno.h dans votre code, sinon c'est l'erreur de compilation assurée !

Une autre manière d'utiliser errno

Utiliser errno de cette façon, c'est bien, mais c'est assez barbant de faire des tests d'égalité, surtout quand on voit qu'il y a environ 40 valeurs d'erreurs possibles à tester... C'est pour cette raison qu'il existe une autre fonction, perror() permettant d'obtenir les erreurs dans un format lisible. :)

void perror(const char* str) ;

La fonction attend une chaîne de caractères qui sera écrite à l'écran (à la manière d'un printf()). Ensuite, perror() se contentera de décrire l'erreur (in English of course).

/* Imaginons que errno ait pris la valeur ENOENT. */ perror("Erreur : "); Erreur : : No such file or directory

Ce qui signifie : Pas de fichier ou de répertoire.

C'est pratique. :) Mais pourquoi y a-t-il deux points en plus ?

Si la chaîne envoyée à perror est nulle, alors les deux points ne seront pas imprimés. Mais si vous envoyez une chaîne de caractères non nulle, ils le seront. :)

Coupler perror et opendir

Maintenant que vous connaissez perror(), tout devient plus simple pour connaître la source d'une erreur !

DIR* rep = NULL; rep = opendir("D:\\Mes Documents\\JaKy"); /* Ouverture d'un dossier */ if (rep == NULL) /* Si le dossier n'a pas pu être ouvert, */ perror(""); /* perror() va nous écrire l'erreur. */

Sachez que errno, c'est encore mieux que ça ! Si vous regardez la documentation de la fonction opendir (man pages), vous pourrez voir une liste de valeurs que errno peut prendre. :)
D'ailleurs, vous pouvez faire de même pour la fonction closedir(). S'il y a échec de cette fonction, errno prendra la valeur EBADF. ;)

Rappelez-vous : errno ne se limite pas à la manipulation des dossiers. D'autres fonctions standards l'utilisent, comme fopen() par
exemple. :)

Manipuler des fichiers à l'intérieur d'un dossier

Après s'être intéressés à errno, nous allons pouvoir coder des choses intéressantes. :) Maintenant que l'on sait ouvrir et fermer un répertoire, pourquoi n'irait-on pas regarder ce qui se trouve dedans ?

La fonction readdir

C'est sûrement la fonction la plus importante du tutoriel. :p Voyons son prototype :

struct dirent* readdir(DIR* repertoire) ;

readdir() prend en paramètre un pointeur sur DIR ; ça, vous connaissez. ;)
Par contre elle renvoie... un pointeur sur une structure dirent. o_O

Eh bien cette structure, elle simule le fichier du dossier qui sera lu ! C'est une structure qui est également définie dans dirent.h. ;) D'ailleurs, encore un peu d'english : dirent signifie DIRectory ENTity, qui signifie entité de répertoire, qui signifie : c'est l'objet que contient un répertoire (dossier, fichiers...).

Pour ne pas vous embrouiller avec le mot entité, j'emploierai le terme de fichier, mais ne faites pas de confusions avec les FILE*. :-°

readdir() renvoie NULL s'il n'y a plus de fichier à lire.

Vous voulez du code ? Parfait. Ce code permet de lire le premier fichier d'un répertoire (je vous passe les directives de préprocesseur) :

int main() { DIR* rep = NULL; struct dirent* fichierLu = NULL; /* Déclaration d'un pointeur vers la structure dirent. */ rep = opendir("D:\\Mes Documents\\JaKy"); if (rep == NULL) exit(1); fichierLu = readdir(rep); /* On lit le premier répertoire du dossier. */ if (closedir(rep) == -1) exit(-1); return 0; }

Après l'appel de readdir, le pointeur de structure fichierLu pointe vers le premier répertoire du dossier JaKy !

Le mot-clé struct me gêne ! J'ai essayé de l'enlever et j'ai une erreur de compilation : dirent undeclared...

En effet, les développeurs n'ont pas fait de typedef à cette structure. Néanmoins, je suis persuadé que vous savez le faire.

Lister le contenu d'un répertoire

C'est bien joli d'utiliser readdir mais... comment savoir quel est le nom du fichier qui est lu ?
C'est bien simple : la structure dirent contient une variable membre, une chaîne de caractères précisément, appelée d_name, qui contient le nom de notre fichier !

fichierLu = readdir(rep); printf("Le fichier lu s'appelle '%s'", fichierLu->d_name); /* On utilise la flèche car fichierLu est un POINTEUR de structure. */

Résultat :

Le fichier lu s'appelle '.'

Gné ? Pourquoi on a droit à un point ?

Le point représente le dossier actuel, celui dans lequel on se trouve ! Si vous connaissez la commande cd, alors :

cd ./Machin/Truc

Ceci devrait vous éclairer l'esprit. :)

Je vous propose maintenant de continuer à lire notre répertoire. On va donc rajouter une 2e fois la fonction readdir().

fichierLu = readdir(rep); printf("Le fichier lu s'appelle '%s'\n", fichierLu->d_name); fichierLu = readdir(rep); printf("Le fichier lu s'appelle '%s'", fichierLu->d_name); Le fichier lu s'appelle '.' Le fichier lu s'appelle '..'

Les deux points représentent le dossier précédent, le dossier qui contient celui dans lequel on se trouve.

Continuons encore un peu. Comme nous voulons lister TOUT le répertoire, il va donc falloir utiliser une boucle qui lira le fichier tant que l'on n'est pas à la fin du dossier... autrement dit, tant que notre fichier est différent de NULL, je l'avais précisé juste avant. ;)

while ((fichierLu = readdir(rep)) != NULL) printf("Le fichier lu s'appelle '%s'\n", fichierLu->d_name);

Normalement vous devriez reconnaître la syntaxe de la boucle while() dans le TP du Pendu et dans l'annexe sur la saisie sécurisée. On lit le répertoire tant qu'il y a quelque chose à lire, et on affiche le nom.

Pour savoir quel fichier readdir() doit lire, un curseur virtuel est créé : à chaque appel de cette fonction(), le curseur avance d'un pas. ;)

Ouvrir un fichier

O.K., maintenant, on sait lire et afficher le contenu de tout le répertoire. Afficher, c'est bien beau, mais pas très utile, alors pourquoi ne pas charger quelque chose, comme un fichier .txt ou un .jpg par exemple ?
La plupart des fonctions permettant de charger une ressource (comme SDL_LoadBMP (de SDL) pour les .bmp, FSOUND_Sample_Load (de FMOD) pour un .mp3, ou même opendir :) ) attendent, comme argument, une chaîne de caractères qui elle-même contient le nom de la ressource à charger.
Quelle coïncidence ! La variable membre de la structure dirent est aussi une chaîne de caractères !
Conclusion : pour charger quelque chose, il suffit juste de passer en paramètre d_name. :D

/* Imaginons que d_name = "image.bmp" */ img = SDL_LoadBMP(fichierLu->d_name);

Si vous utilisez des chemins absolus, vous devez créer une nouvelle chaîne assez grande contenant C:\Program Files\ + leNomDuFichier par exemple, car d_name contient seulement le nom du fichier lu et PAS son arborescence.

Déplacement dans un répertoire

Ça y est ! On a fini le "gros" du tutoriel. Néanmoins, dirent.h propose quelques fonctions utiles à connaître dont il serait impensable que je ne parle pas. :p

Théorie

Cette partie est uniquement théorique, la pratique arrive juste après, je vous expliquerai pourquoi.

Juste avant, je vous ai parlé d'un curseur virtuel, qui s'incrémente à chaque fois que l'on lit un fichier. Je vais vous montrer les quelques fonctions qui permettent de l'utiliser. :)

Où sommes-nous dans le répertoire ?

Il existe une fonction qui nous permet de savoir où se trouve ce curseur. Si vous avez de la mémoire...


Ceux qui ont consulté cette formation ont également consulté...
En voir plus