OVH Cloud OVH Cloud

lire le contenu d'un répertoire en C++

56 réponses
Avatar
oxidor trucidel
Salut,

Je débute en c++, uniquement avec les ressources du net, et
je cherche un moyen de faire lire par mon programme le contenu
d'un répertoire (comme la très classique commande dir).

J'ai cherché sans succès avec google et suis encore occuper
à fouiller les archives de cppfrance.com.

--
Oxidor Trucidel

10 réponses

1 2 3 4 5
Avatar
Pierre Maurette
"oxidor trucidel" a écrit:

Salut,

Je débute en c++, uniquement avec les ressources du net, et
je cherche un moyen de faire lire par mon programme le contenu
d'un répertoire (comme la très classique commande dir).

J'ai cherché sans succès avec google et suis encore occuper
à fouiller les archives de cppfrance.com.
(copie presque carbonne d'une réponse faite sur fr.comp.lang.c)

Votre question n'a pas tout à fait trouvé son forum.
Elle concerne votre OS, peut-être pour vous:
fr.comp.os.ms-windows.programmation
ou la documentation et les forums de votre compilateur. En effet, la
gestion des répertoires n'est pas prise en charge de façon standard ni
par le C ni par le C++.

L'idée de base est d'utiliser trois fonctions qui pourraient s'appeler
FindFirst(), FindNext() et FindClose(). Sous un nom ou un autre, elles
existent sous beaucoup d'OS et dans beaucoup de bibliothèques. Elles
prennent en argument un chemin, et le plus souvent renseignent une
structure qui permet de connaître les caractéristiques (attributs) du
fichier trouvé. Il est possible de filtrer la recherche (*.bak par
exemple).
C'est souvent dans une fonction qui s'appellera récursivement qu'il
faudra les utiliser. Un truc (ni du code, ni de l'algo) tiré de la
fonction que j'utilise sous C++Builder:

void ScanDir(const (chaîne) ActualPath)
{
(chaîne)chemin = opération(ActualPath);
/*par exemple pour ajouter "*.*" */

fini = FindFirst(chemin);
while(!fini && !Abandon)
{
if(/*Le fichier est un répertoire*/)
{
/* traiter */
ScanDir(opération(ActualPath , NomFichier));
/*par exemple pour faire ActualPathNomFichier */
}
else
{
/* traiter */
}

/*traiter ici les messages de l'OS s'il y a lieu
pour éventuellement permettre à l'utilisateur
de forcer l'arrêt (Abandon) */

/*penser ici à gérer un indicateur de progression */

fini = FindNext(/*ID renvoyée par FindFirst*/);
}
FindClose(/*ID renvoyée par FindFirst*/);
return;
}

Selon l'OS, penser à gérer la profondeur de la pile d'appels ou le
stack overflow.
--
Pierre

Avatar
James Kanze
Loïc Joly writes:

|> oxidor trucidel wrote:

|> >>>je cherche un moyen de faire lire par mon programme le contenu
|> >>>d'un répertoire (comme la très classique commande dir).
|> [...]
|> > Le but final de l'opération est, tout simplement, de sauvegarder
|> > des données et de lire le répertoire pour vérifier si des données
|> > s'y trouvent déjà.

|> Dans ce cas là, le plus simple n'est pas de lire le répertoire, mais
|> simplement de relire le fichier.

S'il s'agit de Linux, il existe aussi une fonction access qui permet
d'interroger sur l'existance d'un fichier. Mais...

|> Déjà, si on vérifie le type de retour de close, on doit être pas
|> mal.

|> int main()
|> {
|> ofstream os("PersoDD3.5.txt");
|> os << "Level 25" << endl;
|> if (!os.close())
|> {
|> cout << "Erreur de sauvegarde" << endl;
|> }
|> }

|> Par contre, comme l'OS peut cacher des informations en faisant
|> croire que le fichier est sur disque, ni ta méthode ni la mienne
|> n'offrent une sécurité absolue.

En revanche, le contenu des caches de l'OS seront pris en compte dans
toute autre requête système -- access, readdir, etc.

Aussi dépendant du système, mais il doit y avoir une façon d'enforcer
des write synchrone. Sous Unix, par exemple, il y a fsync ; on peut
aussi specifier que les écritures doivent toutes être synchrones lors de
l'ouverture (requête open).

En revanche, ces genres de manipulations se font au niveau système, et
non avec les fstream. Une solution serait de générer les données avec un
ostringstream, puis d'écrire le dest.str().data(), dest.str().size()
avec la requête système.

Et ne pas oublier de vérifier le code de retour de write et de close:-).

--
James Kanze
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Avatar
James Kanze
drkm writes:

|> Loïc Joly writes:

|> > - Ca n'indique que si la fermeture s'est bien passée, pas s'il y a eu
|> > des problèmes précédent la fermeture.

|> Ce qui était bien le but recherché.

S'il y a eu des problèmes précédemment, qu'est-ce qui aurait rémis le
failbit ou le badbit à 0 ?

|> > - Ca ne met pas à true le failbit d'os, ce qui peut (?) poser des
|> > problèmes.

|> En effet. 27.8.1.7/4 :

|> void close();

|> Effects : Calls rdbuf()->close() and, if that function returns
|> false, calls setstate(failbit)

|> std::basic_filebuf<>::close() ne retourne d'ailleurs pas un booléen,
|> mais un filebuf. Je pense donc que tu as raison, sur la manière de
|> tester la fermeture du fichier.

Sauf que comme Loïc l'a déjà dit, tout ce que ça dit, c'est qu'on a
réussi à passer les données au système. Rien ne garantit qu'elles soient
sur disque. En fait, dans le cadre du C++ portable, il n'y a aucune
façon à le garantir.

--
James Kanze
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Avatar
James Kanze
drkm writes:

|> Loïc Joly writes:

|> > Déjà, si on vérifie le type de
|> > retour de close, on doit être pas mal.

|> Il me semble même que l'on ne doit rien pouvoir faire de plus, en
|> C++ standard.

|> Peut-être peut-on utiliser une fonction

|> bool closeAndWait( std::ofstream & file ) ;

|> qui ferme le fichier et utilise l'API de l'OS pour faire un flush du
|> fichier sur le disque, un équivalent de sync(1) pour Unix.

Je ne crois pas que tu veux dire sync(1), puisque cette commande revient
à l'utilisateur dès que les écritures (physiques, au moins) ont été
schédulée (à moins qu'ils l'ont changé dans les quinze dernières
années). Sous Unix, il existe bien une fonction fsync qui fait ce qu'il
veut ; sous Windows, il y a sans doute quelque chose de semblable. Mais
sous Unix, cette fonction ne connaît ni les ofstream ni les filebuf, il
faut lui passer un descripteur de fichier Unix. Et je parie que sous
Windows, il va vouloir un handle de fichier aussi.

Des bonnes implémentations de la bibliothèque standard aurait fourni une
méthode pour obtenir cette information d'un filebuf -- typiquement, il y
a une fonction supplémentaire pour le faire. Mais toutes les
implémentations ne sont pas si bonnes, et même là, il faut faire
attention à des questions de synchronisation -- appeler fsync sur le
fichier au niveau Unix ne sert à rien si tu n'as pas fait un flush, et
que le filebuf contient encore des données non encore passées au
système. Typiquement, on formatte vers un ostringstream ou un
ostrstream, et on écrit le résultat avec des requêtes système qu'il
faut. (En passant, pour des enrégistrements de longueur fixe, le
ostrstream convient plus qu'un ostringstream.)

--
James Kanze
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Avatar
James Kanze
Christophe de VIENNE writes:

|> > Par contre, comme l'OS peut cacher des informations en faisant
|> > croire que le fichier est sur disque, ni ta méthode ni la mienne
|> > n'offrent une sécurité absolue.

|> Heu, je ne crois pas que cela dérange l'OP. L'info qu'il cherche
|> n'est pas : "mes données sont-elle physiquement sur le disque ?",
|> mais "Y a-t-il un ficher de sauvegarde que je peux lire dans tel
|> dossier".

On pourrait le prendre les deux façons. Mais puisque la réponse à la
question que tu poses est tellement évidente : on essaie de lire le
fichier, et si l'ouverture échoue, c'est qu'il n'y était pas.

--
James Kanze
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Avatar
drkm
James Kanze writes:

drkm writes:

|> Loïc Joly writes:

|> > - Ca n'indique que si la fermeture s'est bien passée, pas s'il y a eu
|> > des problèmes précédent la fermeture.

|> Ce qui était bien le but recherché.

S'il y a eu des problèmes précédemment, qu'est-ce qui aurait rémis le
failbit ou le badbit à 0 ?


Quel failbit et quel badbit ? Je parlais de
std::basic_filebuf<>::close().

|> > - Ca ne met pas à true le failbit d'os, ce qui peut (?) poser des
|> > problèmes.

|> En effet. 27.8.1.7/4 :

|> void close();

|> Effects : Calls rdbuf()->close() and, if that function returns
|> false, calls setstate(failbit)

|> std::basic_filebuf<>::close() ne retourne d'ailleurs pas un booléen,
|> mais un filebuf. Je pense donc que tu as raison, sur la manière de
|> tester la fermeture du fichier.

Sauf que comme Loïc l'a déjà dit, tout ce que ça dit, c'est qu'on a
réussi à passer les données au système. Rien ne garantit qu'elles soient
sur disque.


Tester si un fichier a bien été fermé est évidemment dépendant du
système. Je veux dire que cela dépend de ce que le système entend par
une fermeture de fichier. Ce qui peut être « la buffre list est à
jour, mais pas nécessairement le disque ».

En fait, dans le cadre du C++ portable, il n'y a aucune
façon à le garantir.


Oui.

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html

Avatar
drkm
James Kanze writes:

drkm writes:

|> qui ferme le fichier et utilise l'API de l'OS pour faire un flush du
|> fichier sur le disque, un équivalent de sync(1) pour Unix.

Je ne crois pas que tu veux dire sync(1), puisque cette commande revient
à l'utilisateur dès que les écritures (physiques, au moins) ont été
schédulée


Tu veux dire qu'elle prépare les requêtes d'écriture sur le disque
(ou même seulement qu'elle dit qu'il faut les envoyer au plus vite),
mais qu'elle n'attend pas leur accomplissement ?

J'ai toujours cru qu'elle ne revenait que lorsque les données
étaient effectivement sur le disque.

Je ne suis pas sur Unix pour l'instant, mais j'ai trouvé cet extrait
sur le web, un extrait de la page sync(2) d'une version de Linux :

According to the standard specification (e.g., SVID), sync()
schedules the writes, but may return before the actual writing is
done. However, since version 1.3.20 Linux does actually wait.
(This still does not guarantee data integrity: modern disks have
large caches.)

Je ne savais pas. Merci.

--drkm, en recherche d'un stage : http://www.fgeorges.org/ipl/stage.html

Avatar
Arnaud Debaene
drkm wrote:
James Kanze writes:

drkm writes:

qui ferme le fichier et utilise l'API de l'OS pour faire un flush
du fichier sur le disque, un équivalent de sync(1) pour Unix.



Je ne crois pas que tu veux dire sync(1), puisque cette commande
revient à l'utilisateur dès que les écritures (physiques, au moins)
ont été schédulée


Tu veux dire qu'elle prépare les requêtes d'écriture sur le disque
(ou même seulement qu'elle dit qu'il faut les envoyer au plus vite),
mais qu'elle n'attend pas leur accomplissement ?

J'ai toujours cru qu'elle ne revenait que lorsque les données
étaient effectivement sur le disque.


Selon les disques physiques utilisés, ce n'est pas tojours possible : la
norme ATA ne prévoit pas par exemple de signal remontant du disque quand une
écriture est terminée, et comme le disque peut très bien avoir un cache
local, il n'y a aucun moyen d'être notifié quand l'écriture est terminée.

Arnaud




Avatar
kanze
drkm wrote in message
news:...
James Kanze writes:

drkm writes:

|> qui ferme le fichier et utilise l'API de l'OS pour faire un
|> flush du fichier sur le disque, un équivalent de sync(1) pour
|> Unix.

Je ne crois pas que tu veux dire sync(1), puisque cette commande
revient à l'utilisateur dès que les écritures (physiques, au moins)
ont été schédulée


Tu veux dire qu'elle prépare les requêtes d'écriture sur le disque
(ou même seulement qu'elle dit qu'il faut les envoyer au plus vite),
mais qu'elle n'attend pas leur accomplissement ?


Tout à fait. Elle met les requêtes d'écriture physique dans la queue du
support physique, mais c'est tout.

Je ne sais pas trop à quoi elle sert aujourd'hui. Quand je m'en servais,
il y a vingt ans, elle servait surtout quand tu réparais un disque
corrompu à la main, en corrigeant le contenu des secteurs système. Quand
tu faisais ça, évidemment, il n'y avait pas d'autres utilisateurs sur le
système, et tu arrêtais tous les démons et d'autres programmes
non-essentiels -- en gros, les seules écritures en suspence étaient les
tiennes. Une fois finies les corrections, il fallait rébooter.
Seulement, par défaut, le système de l'époque ne faisait un sync que
toutes les trentes secondes. Plutôt que d'attendre trente secondes, on
invoquait sync. Ensuite, le temps nécessaire pour que la main se déplace
du clavier au bouton de reset était largement suffisant pour que les
écritures programmées aient lieu. (En fait, on entendait quand le disque
travaillait, et on attendait qu'il redevient silent.)

Il n'a jamais garanti « Synchronized I/O Data Integrity » ni
« Synchronized I/O File Integrity », au sens qu'elles sont définies dans
la norme Posix.

J'ai toujours cru qu'elle ne revenait que lorsque les données
étaient effectivement sur le disque.


Non. Pour ça, il faut soit la requête fsync, soit que la mode O_SYNC ou
O_DSYNC soit spécifiée dans l'open ou dans un appel à fcntl par la
suite. En tout cas, l'integrité des écritures n'est assurée que fichier
par fichier, et que par rapport à l'ouverture qui a donné le descripteur
de fichier. (Pour le cas où il y a plusieurs processus qui partage le
même fichier, et qu'on veut que le processus qui lit voit toujours le
dernier état écrit, il faut aussi utiliser l'option O_RSYNC.)

Dans la pratique, il y a la garantie, et il y a ce qui se passe. S'il
n'y a aucun problème pour des disques locaux, il en est tout autrement
pour les systèmes mountés à distance. En principe, NFS fournit les
primitifs nécessaires pour implémenter la synchonisation complète ; dans
la pratique, ils ne sont pas implémentés par toutes les implémentations
de NFS, et évidemment, il existe aussi d'autres protocols ; je ne sais
pas, par exemple, ce qu'offre SMB ou AFS à cet égard. En programmeur
prudent, quand j'ai besoin d'une sémantique de transaction garantie et
persistente, je m'assure que les données sont sur un disque local (ou,
dit autrement, que le processus qui assure l'écriture tourne sur la
machine où se trouve le disque).

Je ne suis pas sur Unix pour l'instant, mais j'ai trouvé cet extrait
sur le web, un extrait de la page sync(2) d'une version de Linux :

According to the standard specification (e.g., SVID), sync()
schedules the writes, but may return before the actual writing is
done. However, since version 1.3.20 Linux does actually wait.
(This still does not guarantee data integrity: modern disks have
large caches.)


C'est bien le « may return before the actual writing is done » dont je
me souvenais. Mais mes souvenirs datent d'avant SVID ; j'imaginais qu'il
ait pu avoir des changements entre-temps.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
"M. B." wrote in message
news:<cfknqj$31s$...
"Loïc Joly" a écrit dans le message de
news: cfkfl9$oor$

M. B. wrote:

Ce qui est con, c'est que si tu avais précédé ton post en informant
que c'était pas du C++ standard, car une solution standard n'existe
pas, et que donc tu donne une solution VC++.NET only, et si cette
solution, tu l'avais donnée en C++.NET (ce qui est trivial à partir
de l'écriture C#), tu aurais pu faire un post intéressant.


Il n'y a aucun interet a utiliser C++ sur .NET


C-à-d qu'il n'y a aucun intérêt alors d'utiliser .NET ?

Nous, on utilise C++ sur .NET. En fait, à peu près un tiers du code dans
mon projet va servir sur la nouvelle cliente en .NET. Mais il faut
toujours qu'il continue à compiler aussi sous Solaris. Alors, d'après le
peu que j'ai vu, l'intérêt réel de .NET, c'est précisement la possibilté
d'utiliser une partie du code en commun avec des applications en C++ sur
Sparc et Linux. L'alternatif était Java, et là... pas assez de
performance pour notre type d'application côté serveur, sous Solaris et
Linux, et pas de possibilité d'utilisation d'une base de code commun
entre les deux plateformes si la partie Solaris/Linux était en C++ --
JNI est trop lourd, trop lent, et trop difficile à mettre en oeuvre.

Enfin, c'est que ceux qui ont fait l'analyse ici ont trouvé.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


1 2 3 4 5