XL 2010 Copier un fichiers txt qui ont le même nom de plusieurs sous-dossiers vers un dossier

hamzaelhathout

XLDnaute Nouveau
Bonjour,

J'ai un dossier qui contiens plusieurs sous-dossier pour chaque journée qui ont pour nom "aaaa-mm-jj".
Dans ces sous-dossier, il y a un rapport au format txt qui a toujours le même nom "XXXXX.txt"

Je voudrais créer une macro qui aille chercher ce fichier txt dans chaque sous dossier et me le copie avec comme nom, celui du sous dossier dans lequel il se trouve (donc aaaa-mm-jj.txt) vers un dossier "destination".

J'ai cherché dans le forum mais je n'ai pas trouvé exactement ça.

Merci d'avance.
 
Solution
Super merci.

J'ai adapté pour le nom du fichier exact.

VB:
Option Explicit

Sub Test()
    Call FichiersSousRépertoires("C:\Users\Youssef\Documents\fansub\testmacro\")
End Sub

'---------------------------------------------
'Fichiers des sous-répertoires d'un répertoire
'---------------------------------------------
Sub FichiersSousRépertoires(NomRépertoire As String)
    Dim oFSO As Object
    Dim oDir As Object
    Dim oSubDir As Object
    Dim oFile As Object

    'File System Object
    Set oFSO = CreateObject("Scripting.FileSystemObject")

    'Directory Object
    Set oDir = oFSO.GetFolder(NomRépertoire)

    'Parcours des sous-répertoires du répertoire
    For Each oSubDir In oDir.SubFolders
        'Parcours des fichiers du...

Dudu2

XLDnaute Barbatruc
Sur une recherche sur extension "xls*" parmi 70.000 fichiers, je trouve le même nombre et plus étonnamment pratiquement le même temps pour FSO et pour VBA DIR.
1612867687967.png

1612867688083.png


Et pour une recherche sur extension "txt"
1612867918564.png
1612867918680.png
 

patricktoulon

XLDnaute Barbatruc
re

heu...70000 moi je parle de C et tout ses dossiers interdits et qui contient chez moi plus de 250000 fichiers

604 fichier dans une variable tableau c'est du pipi de chat ca on l'a vu fso devient rapide

d'ailleurs chez moi si fso dure 31 dir 2 à 3 fois moins(voir beaucoup plus différent la différence sur un autre disque que C

donc de 233 s je passe a 24 pour 4450 fichiers trouvés sur 250000
d'ailleurs je viens de re de tester avec ma fonction DIR vba sur C et j'en trouve autant mais en 24 secondes

:) ;)
seul dir bath me rend les 7477 fichiers en encore moins de temps

la FONCTION dir vba qui me ramène les 4450 de C comme ton FSO #194
VB:
'patricktoulon dir fichier fonction récursive
Option Explicit
Sub testDIR()
    Dim tim#, T
    [A1].CurrentRegion.Clear
    tim = Timer
    T = DirList("c:\")
    If IsArray(T) Then
        MsgBox Timer - tim & " secondes pour " & UBound(T) & " fichier(s)"
        [A1].Resize(UBound(T)) = Application.Transpose(T)
    Else
        MsgBox "pas de fichier"
    End If
   
End Sub
Function DirList(dossier As String, Optional recall As Boolean = False) As Variant
    Dim ItemVu As String, SubFolderCollection As New Collection, I As Long, a As Long, q As Long, criteres, arr1, arr2, subdossier
    Static tbl$()    'tbl est statique
    If recall = False Then ReDim tbl(1 To 1)    ' si recall  on redim un tableau  de zero item (pour la creation du tableau)
    criteres = vbDirectory Or vbSystem Or vbHidden Or vbArchive Or vbReadOnly Or vbNormal
    On Error Resume Next    'gestion des fichiers dossiers system et interdit ou generant une erreur(PerLog,recycle,etc..)
    ItemVu = Dir(dossier, criteres)
    If Error.Number = 0 Then    ' si pas d'erreur on examine le contenu
        'examen  du dossier courrant
        Do While ItemVu <> vbNullString    'boucle tant que DIR renvoie une chaine
            If ItemVu <> "." And ItemVu <> ".." Then
                On Error Resume Next
                If (GetAttr(dossier & ItemVu) And vbDirectory) = vbDirectory Then    'test Dossier
                    If Err.Number = 53 Then
                        If ItemVu Like "*.txt" Then ReDim Preserve tbl(1 To a): tbl(a) = dossier & ItemVu: a = UBound(tbl) + 1:    'ajout fichier dans la liste
                    End If
                    SubFolderCollection.Add dossier & ItemVu    'sinon ajout dans la collection de dossier
                    Err.Clear
                Else    'sinon c'est un fichier et pas un concombre:)
                    If ItemVu Like "*.txt" Then ReDim Preserve tbl(1 To a): tbl(a) = dossier & ItemVu: a = UBound(tbl) + 1:    'ajout fichier dans la liste
                End If
            End If
            ItemVu = Dir()
        Loop
    Else
        Err.Clear
    End If
    'examen des sub dossier appel récursif
    For Each subdossier In SubFolderCollection
        DirList subdossier & "\", True
    Next subdossier
    DirList = False
    If Not recall Then DirList = tbl  ' return du tableau (apres le dernier appel récursif )'economie de 0.3000 secondes
End Function
 

patricktoulon

XLDnaute Barbatruc
ce fut un plaisir @Dudu2 comme d'hab' :)
j'ai appris pas mal notamment la notion de l'entité d'une instance d'un appel récursif et comment l'identifier
c'est une question que je m’étais posé il y a longtemps justement pour éviter le moulinage et les répétions de redim qui peuvent être lourde mais sans jamais vraiment chercher de réponse alors qu'au final comme dans cet exercice c'est important
je garde quand même ta #194
ne serait-ce que pour l'a ré accélérer avec un dir mieux pensé ou autre méthode
 

patricktoulon

XLDnaute Barbatruc
re
voila j'avais un doute il est dissipé on est toujour à 4450 fichier"*.txt" sur C mais on est revenu a 40 secondes
sur C avec 250000 fichiers
le pire c'est que je le savais 🤣🤣🥳
le problème avec dir c'est quoi? :
Code:
if Dir(oDir.Path & "\*." & Extension) <> vbNullString

[parentheses pour ceux qui ne le savaient pas ]
alors oui en effet dir n'est pas récursif c'est pour cela que l'on a besoin d'un do/ loop pour lister tout un dossier
[/parentheses]
c'est qu'il est jamais fermé avant que l'instance dans la quelle il est invoqué sub ou fonction soit terminée
de la même manière qu'un bloc with anonyme (with createobject(" trucbidule")..... :end with] n'est pas éliminé de la mémoire avant la fin de la macro ou de la session de la fonction

ce qui fait que
que dir(blablabla) et dir (tototo) correspondrait a dire
dir(blablabla) et dir comme a la fin d'un do loop et donc dir (toto) me donne le 2d de dir(blabla)
et oui dir sur dir ben y a coquillette dans le potage vba en perds son en son latin
il faut donc variabliliser le dir et l'annuler si error ou vbnullstring ou len(0)
c'est comme vous voulez (chacun sa méthode pour le return du dir )


ainsi chaque session de la fonction a son dir propre et les oies et les canards s'en donnent a cœur joie

donc une Nieme et là il s'agit bien de tester sur C les fichier ".txt"

donc le code #194 avec le dir

VB:
'Option Explicit

Sub TestFichiersRépertoireFSO()
    Dim Table As Variant, tim#
    Const Répertoire = "c:"    ' "H:\Téléchargements"
    tim = Timer
    'Table = FichiersRépertoireFSO(Répertoire, , "txt")
    Table = FichiersRépertoireFSO(Répertoire, , "txt")

    'If not VarType(Table) = vbBoolean Then
    If IsArray(Table) Then
        Table = TransposeExcel(Table)
        MsgBox UBound(Table) & " fichier(s) trouvé(s) dans le répertoire <" & Répertoire & "> en " & Timer - tim & " s/"
        ActiveSheet.Range("A1:A" & Rows.Count).ClearContents
        ActiveSheet.Range("A1").Resize(UBound(Table)).Value = Table
    Else
        MsgBox "Aucun fichier dans le répertoire <" & Répertoire & ">"
    End If
End Sub

'---------------------------------------------------------------
'Liste des fichiers de l'arborescence complète d'un répertoire
'par la "méthode FileSystemObject"
'
'- NomRépertoire: chaine du nom du répertoire concerné
'                 (avec ou sans '\' final)
'- NoRecycle: True (valeur par défaut) pour ne pas avoir les
'             fichiers de la poubelle dans la liste résultat
'             si NomRépertoire est une lettre de lecteur (drive)
'- Extension: "Pattern" / modèle de l'extension des fichiers à
'             sélectionner ("txt", "xls*" ou "" pour tous)
'- Return: table à 1 dimension des noms complets des fichiers
'          ou False si aucun fichier dans le répertoire
'---------------------------------------------------------------
Function FichiersRépertoireFSO(ByVal NomRépertoire As Variant, _
                               Optional NoRecycle As Boolean = True, _
                               Optional Extension As String = "") As Variant
'Tableau résultat static pour être indépendant des appels récursifs
    Static TabNomsFichiers() As String
    Static NbFichiers As Long

    'Variable du FileSystemObject commune à toutes les instances de la fonction
    Static oFSO As Object

    'Variable spécifiques à une instance de la fonction
    Dim oDir As Object
    Dim oSubDir As Object
    Dim oFile As Object
    Dim InitialCall As Boolean
    Dim TakeIt As Boolean
    Dim NomObjetEnErreur As String
    Dim x As Variant
  
    'Appel recursif de cette fonction (par elle-même ci-dessous)
    If TypeOf NomRépertoire Is Object  Then
        InitialCall = False

        'Valorise l'objet Folder
        Set oDir = NomRépertoire

        'Appel initial
    Else
        InitialCall = True

        'Table résultat
        Erase TabNomsFichiers
        NbFichiers = 0

        'File System Object
        Set oFSO = CreateObject("Scripting.FileSystemObject")

        'Complémente éventuellement le nom du répertoire avec '\'
        If Right(NomRépertoire, 1) <> "\" Then NomRépertoire = NomRépertoire & "\"

        'Valorise l'objet Folder
        Set oDir = oFSO.GetFolder(NomRépertoire)
    End If

    'Si option NoRecycle et répertoire poubelle on ne traite pas
    If (NoRecycle And oDir.Name = "$RECYCLE.BIN") Then Exit Function

    'Si le répertoire est "System Volume Information" on ne traite pas
    If oDir.Name = "System Volume Information" Then Exit Function

    'Vérifie si le répertoire contient des fichiers avec l'extension
    If Len(Extension) = 0 Then
        TakeIt = True
    Else
        TakeIt = True
        'La fonction Dir() introduit des erreurs #52 ou #53 sur les objets Folder
        'de la boucle "For Each oSubDir In oDir.subfolders" ci-dessous.
        'IL NE FAUT PAS L'UTILISER !!!
        'SI SI IL FAUT L UTILISER !!!!!! :):):):)
        On Error Resume Next
        x = True
        x = Dir(oDir.Path & "\*." & Extension) <> vbNullString
        If Err.Number > 0 Or Not x Then TakeIt = False: Err.Clear: x = vbNull
    End If


    'On n'examine les fichiers du répertoire que s'il contient des fichiers avec l'extension
    If TakeIt Then
        On Error Resume Next
        For Each oFile In oDir.Files
            If Err.Number = 0 Then
                'Test si correspondance de l'extension
                If Len(Extension) = 0 Then
                    TakeIt = True
                Else
                    If oFSO.GetExtensionName(oFile.Name) Like Extension Then TakeIt = True Else TakeIt = False
                End If

                'Stocke le nom complet du fichier en table
                If TakeIt Then
                    NbFichiers = NbFichiers + 1
                    ReDim Preserve TabNomsFichiers(1 To NbFichiers)
                    TabNomsFichiers(NbFichiers) = oFile.Path
                End If

                'Fichier en erreur
            Else
                NomObjetEnErreur = "Fichier <"
                If oFile Is Nothing _
                   Then NomObjetEnErreur = NomObjetEnErreur & "Nothing" & ">" _
                   Else NomObjetEnErreur = NomObjetEnErreur & oFile.Name & ">"
                GoSub TraiteErreur
            End If
        Next oFile
        On Error GoTo 0
    End If

    'Parcours des sous-répertoires du répertoire en cours
    On Error Resume Next
    For Each oSubDir In oDir.subfolders
        If Err.Number = 0 Then
            'Appels recursifs identifiés par le type Object de l'argument OsubDir
            Call FichiersRépertoireFSO(oSubDir, NoRecycle, Extension)

            'Répertoire en erreur
        Else
            NomObjetEnErreur = "Répertoire <"
            If oSubDir Is Nothing _
               Then NomObjetEnErreur = NomObjetEnErreur & "Nothing" & ">" _
               Else NomObjetEnErreur = NomObjetEnErreur & oSubDir.Path & ">"
            GoSub TraiteErreur
        End If
    Next oSubDir
    On Error GoTo 0

    'Return value
    If InitialCall Then
        FichiersRépertoireFSO = False
        If NbFichiers > 0 Then FichiersRépertoireFSO = TabNomsFichiers
    End If
    Exit Function

TraiteErreur:
    'Error #70 Authorisation refusée, Error #76 Path not found, Autre erreur à identifier ?
    If Not (Err.Number = 70 Or Err.Number = 76) Then
        MsgBox "FichiersRépertoireFSO erreur #" & Err.Number & vbCrLf & "Sur " & NomObjetEnErreur & ""
    End If
    NomObjetEnErreur = ""
    Err.Clear
    Return
End Function

'--------------------------------------------------------------------
'Fonction de Tranpose selon la logique de WorksheetFunction.Transpose
'sauf que WorksheetFunction.Transpose se limite à 65536 éléments
'alors que cette fonction lève cette limite.
'--------------------------------------------------------------------
Function TransposeExcel(T As Variant) As Variant
    Dim tt() As Variant
    Dim NbDimensions As Integer
    Dim i As Long
    Dim j As Long

    If Not IsArray(T) Then
        MsgBox "Function TransposeExcel: error argument is not an array !"
        Exit Function
    End If

    '1 ou 2 dimensions pour t ?
    On Error Resume Next
    i = UBound(T, 2)
    If Err.Number Then NbDimensions = 1 Else NbDimensions = 2
    On Error GoTo 0

    '------------------------------------------------------
    'Tableau origine 1 dimension
    '=> Tableau destination 2 dimensions dont la 2ème est 1
    '------------------------------------------------------
    If NbDimensions = 1 Then
        ReDim tt(LBound(T) To UBound(T), 1 To 1)

        For i = LBound(T) To UBound(T)
            tt(i, 1) = T(i)
        Next i
    End If

    '----------------------------
    'Tableau origine 2 dimensions
    '----------------------------
    If NbDimensions = 2 Then
        '-----------------------------------------------
        'Tableau origine 2 dimensions dont la 2ème est 1
        '=> Tableau destination 1 dimension
        '-----------------------------------------------
        If UBound(T, 2) = 1 Then
            ReDim tt(LBound(T, 1) To UBound(T, 1))

            For i = LBound(T, 1) To UBound(T, 1)
                tt(i) = T(i, 1)
            Next i

            '-------------------------------------------------
            'Tableau origine 2 dimensions dont la 2ème est > 1
            '=> Tableau destination 2 dimensions inversées
            '-------------------------------------------------
        Else
            ReDim tt(LBound(T, 2) To UBound(T, 2), LBound(T, 1) To UBound(T, 1))

            For i = LBound(T, 2) To UBound(T, 2)
                For j = LBound(T, 1) To UBound(T, 1)
                    tt(i, j) = T(j, i)
                Next j
            Next i
        End If
    End If

    TransposeExcel = tt
End Function

voila les amis :)
purée j'ai honte je le savais 🤪🥺
maintenant vous savez pour quoi l'erreur 52 alors qu'il ne devrait pas
tout simplement par ce que le résultat du dir n'est pas celui de l'instance
bouhhh!! j'ai bien bossé moi ;.. café 😊
 

Dudu2

XLDnaute Barbatruc
Tu as mis en évidence une nouvelle chose étrange qui m'échappe (une de plus).
En fait ce code suffit à ne pas se prendre les erreurs.
VB:
 Else
        'La fonction Dir() introduit des erreurs #52 ou #53 sur les objets Folder
        'de la boucle "For Each oSubDir In oDir.subfolders" ci-dessous.
        'IL NE FAUT PAS L'UTILISER !!!
        'SI SI IL FAUT L UTILISER !!!!!! :):):):)
        On Error Resume Next
        TakeIt = Len(Dir(oDir.Path & "\*." & Extension)) > 0
        If err.Number <> 0 Then TakeIt = True
        On Error GoTo 0
    End If

Ce qui est incompréhensible, c'est que:
- Si on met le On Error Resume Next devant le Dir() on se récupère des erreurs #52 ou #53.
- Si on NE met PAS le On Error devant le Dir(), le Dir ne se plante pas et on récupère les erreurs #52 ou #53 par la suite alors qu'elles ne sont pas liées aux instructions qu'on était sensé couvrir par le On Error Resume Next.

Donc le Dir() génère des erreurs qui si elles ne sont pas interceptées par un On Error Resume Next ne font pas planter le VBA. Très intéressant !

Suite à tes essais, j'ai rajouté le Dir() encadré par On Error en cas recherche sur extension.
Les Post #194 et Post #72 ont été pour la nième fois mis à jour pour refléter cette modification.
 
Dernière édition:

patricktoulon

XLDnaute Barbatruc
re
oui le numérique du boolean ca marche aussi
oui de toute façon on le sait que dir vba a des faiblesses
et là comme tu fait tu variabilise le dir "takeit=len(dir..........)>0"

le fait de faire
on error resume next
x=dir ....blablabla
if err.number >0 then.....

te permet de renouveler la variable au prochain tour car "x= ..." est catégoriquement changé a chaque fois

tandis que
on error resume next
if dir(blablabla)<>"" then
if err.number=blablabla

va te planter au tour suivant car le dir n'est pas fermé et ce qui va ressortir c'est l'item N°2 de la session précédente et comme on est sur un new dir ben c'est plus cohérent vba n'a pas d'autre choix que de planter

en gros
x=dir(blabla)<>""---> terminé c'est oui ou c'est non ou error

if dir(blalabla) --> c'est un dir anonyme qui n'a pas de variable d'instance et donc pour le gérer ben walouh
bien sur si error elle sera la même que pour celui variabilisé
mais tu ne peu pas gérer les 3 état (oui,non,error) meme avec un if else car le dir n'est pas fermé
X lui est dimé dans chaque instance

autrement dit c'est une porte sans poignée (sans handle object) que tu ouvre mais que tu ne peut plus fermer jusqu'au courant d'air du clap de fin de session de la fonction


elle est là l'erreur depuis le début ;)

je reconnais que c'est tordu 🤣
 

Dudu2

XLDnaute Barbatruc
ce qui va ressortir c'est l'item N°2 de la session précédente
Ça il faudrait le prouver. Si tu fais 2 ou 3 ou 10 Dir("H:\*.txt") à la suite dans une fonction, que se passe-t-il ?
N'est-ce pas plutôt le Dir() tout seul qui passe au 2ème puis au 3ème item etc... de la liste ?

Edit: Je me suis trompé dans l'extrait de code ci-dessus en #233 et j'ai corrigé.
 

patricktoulon

XLDnaute Barbatruc
re non dir VBA n'est pas récursif
pour la recursivité fich1,fich2,etc....
il faut faire un do loop
item=dir("c:\mondossier &"\"*.*") ' dir sur fichier uniquemement
do while item<>vbnullstring
msgbox item
item=dir 'on rapelle item soit le dirqui contient les argument de départ
loop

maintenant que ce passe til dans notre fonction recursive fso
le dir de l'apel2 se mélange avec le dir de l'apel1 si utilisation de if dir(.....)
autrement dit l'apel2 fait le recurse du dir mais comme il est argumenté comme le premier appel ben c'est le boxon
pourquoi
ben en fait tout les dire se ferme a la fin de la session
quand la 1 appelle le subfolder 1 elle a un dir quand la 2 demarre elle a son dir

autrement dit dir dir dir etc....
tandis que variable=dir()...
le variable est redimé en début d'instance donc le dir précédent reste ouvert mais n'est plus variable
le variable de la 1 qui est devenu un dir anonyme dans l'instance 2
se fermera quand l'instance 2 de la fonction rendra la main a l'instance 1 et que ce dir désormais anonyme
c'est pas plus compliqué

Code:

c'est juste une histoire de variabilisation

allez pour t'en convaincre une bonne fois pour toute fait ce test sans débloquer la ligne et un 2d en la débloquant

tiens je refait le test en condition équivalente a notre fonction récursive a savoir 2 dossiers différent

regarde bien les message
pour arrêter reste appuyé sur enter en cliquant sur mode création dans le ruban pour arrêter vba
Code:
Sub testdirdegogole()
Item = Dir("h:\*.*") ' dir sur fichier uniquemement

Do While Item <> vbNullString


MsgBox Item

Item = Dir("e:\*.*") ' dir sur fichier uniquemement

Item = Dir 'on rapelle item soit le dirqui contient les argument de départ

Loop
End Sub
 
Dernière édition:

Dudu2

XLDnaute Barbatruc
L'appel de Dir(), à partir du moment où il contient un argument non vide ré-initialise sa pile sans provoquer d'erreur (voir exemple ci-dessous). Dans le code du FSO on ne fait jamais appel à Dir() avec un argument vide. Donc je ne vois pas le problème. Il n'y a pas de dépendance entre 2 appels de Dir(). C'est juste un appel de fonction classique.
VB:
Sub a()
    Dim b As Boolean
    Dim i As Integer
    Dim TabRep() As Variant
    Dim Rep As String
 
    TabRep = Array("H:", "H:\Téléchargements", "H:\TEMP")
 
    For i = 1 To 3
        On Error Resume Next
        Rep = TabRep(i - 1) & "\*.txt"
        b = Len(Dir(Rep)) > 0
        MsgBox Rep & " = " & b & ", Err.Number = " & Err.Number
        On Error GoTo 0
    Next i
End Sub

Personnellement je ne comprends pas d'où viennent ces erreurs sur Dir(). Il n'y a aucune raison pour qu'un Len(Dir()) parte en erreur, et encore moins que cette erreur ne plante pas VBA mais qu'elle soit détectée par un On Error. Ce n'est pas "propre" comme comportement. Je soupçonne le mélange avec FSO mais aucune idée du pourquoi.
 

Dudu2

XLDnaute Barbatruc
C'est pareil. De plus je pense que le contexte du Dir() est spécifique à l'instance de la fonction.
Quand bien même il ne le serait pas, comme on ne l'exploite jamais avec un Dir() vide pour avoir les fichiers suivants il n'y a pas de question à se poser.

Tu veux du récursif ? Voilà.
VB:
Sub a()
    Call x(1)
End Sub

Sub x(ByVal NoAppel As Integer)
    Dim b As Boolean
    Dim i As Integer
    Static TabRep() As Variant
    Dim Rep As String
   
    If NoAppel = 1 Then
        TabRep = Array("H:", "H:\Téléchargements", "H:\TEMP")
    End If
   
    On Error Resume Next
    Rep = TabRep(NoAppel - 1) & "\*.txt"
    b = Len(Dir(Rep)) > 0
    MsgBox Rep & " = " & b & ", Err.Number = " & Err.Number
    On Error GoTo 0
   
    NoAppel = NoAppel + 1
    If NoAppel <= 3 Then Call x(NoAppel)
End Sub

Le problème vient d'ailleurs
1612900444811.png
 

Dudu2

XLDnaute Barbatruc
En fait je pense que les erreurs #52 et #53 ont pour origine le même problème rencontré précédemment sur les noms de fichiers Unicode. Mais cette fois ce sont les noms des répertoires récupérés de FSO (oDir.Path) qui sont concernés dans l'instruction TakeIt = Len(Dir(oDir.Path & "\*." & Extension)) > 0.

Mais je reste toujours étonné que le Dir() en erreur:
1 - ne plante pas VBA dans ces cas là quand non couvert par un On Error,
2 - que l'erreur se déporte malgré un reset de la gestion d'erreur avec un On Error GoTo 0 juste après,
3 - qu'on doive / puisse la détecter avec un On Error sur le Dir() pour empêcher le déport de l'erreur.
Mais bon c'est comme ça.
 
Dernière édition:

Discussions similaires

Statistiques des forums

Discussions
314 645
Messages
2 111 527
Membres
111 190
dernier inscrit
clmtj