Search & Replace dans TOUS les fichiers d'un dossier. [Résolu]

Signaler
Messages postés
82
Date d'inscription
jeudi 26 décembre 2013
Statut
Membre
Dernière intervention
14 juin 2016
-
Messages postés
15172
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
1 décembre 2020
-
Bonjour tout le monde,

j'aimerais recréer le mode de recherche "Find in files" de Notepad++ pour pouvoir faire un search and replace dans tous les fichiers (et supporter toutes les extensions, pas seulement les fichiers .txt)

Dim Dossier As DirectoryInfo = New DirectoryInfo(TextBox1.Text)
For Each Fichier As FileInfo In Dossier.GetFiles
    Dim texte As String = ""
    Using sr As StreamReader = New StreamReader(Fichier.FullName)
        texte = sr.ReadToEnd
        sr.Close()
    End Using
Next


Le problème est que mon logiciel trouve beaucoup moins d'occurences du mot à chercher que Notepad++, qui fait très bien son boulot !
Le problème vient surement du fait que mon logiciel ne lit pas autant de fichiers que Notepad++, ou bien il ne va pas plus loin que les fichiers qui sont à la racine du dossier voulu...

Si le problème est le précédent, comment puis-je faire pour regarder tous les fichiers, meme ceux contenus dans les sous-dossiers ??

Merci d'avance

7 réponses

Messages postés
15172
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
1 décembre 2020
465
Bonsoir,

il faut commencer par lister tous les fichiers présents, regarde du coté de Directory.GetFiles().
Ensuite pour la lecture de fichiers Ascii, je préfère utiliser la classe File. Elle permet de lire une ligne, toutes les lignes dans un tableau, tout le texte etc...
Avec une Regex tu fais ton cherche/remplace, l'utilisation de la regex permet de paramétrer si tu tiens comptes de la casse, si le mot doit être entier, les accents etc...
Et ensuite avec File.Save() tu enregistres.

Messages postés
82
Date d'inscription
jeudi 26 décembre 2013
Statut
Membre
Dernière intervention
14 juin 2016

Merci je vais essayer de faire ceci ;)
Messages postés
15172
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
1 décembre 2020
465
Bonjour,

un petit exemple vite fait (attention il ne marche pas tout à fait)

    Private Sub FindReplace()
        Dim path As String = Application.StartupPath & "\test"
        Dim txtFiles() As String = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories) 'répère le nom des fichiers dans le répertoire et sous les répertoires
        Dim mot As String = "coucou" 'le mot à trouver
        Dim remplacement As String = "Remplacé"

        For Each fichier As String In txtFiles
            Dim texte As String = File.ReadAllText(fichier)
            Dim motEntierRespecteLaCasse As New Regex("(^|\W)" & mot & "($|\W)") 'le patern dit que l'on cherche le mot avec un début de texte ou un caractères non alphanumérique avant et un fin de texte ou un caractère non alpha numérique après
            'si on utilise le Replace(string, string) les eventuels espaces ou signes de ponctuation avant et apres seront remplacé, il faut donc délégué une fonction qui précise que l'on ne remplace que le mot
            Dim texte1 As String = motEntierRespecteLaCasse.Replace(texte, Function(match) match.Value.Replace(mot, remplacement)) 'retourne le texte trouvé par la regex dans lequel on change mot par remplacement

            'le patern dit que l'on cherche le mot avec un début de texte ou un caractères non alphanumérique avant et un fin de texte ou un caractère non alpha numérique après et on ignore la case
            Dim texte2 As String = Regex.Replace(texte, "(^|\W)" & mot & "($|\W)", Function(match)
                                                                                       Dim resultat As String = Regex.Replace(match.Value, mot, remplacement, RegexOptions.IgnoreCase)
                                                                                       Return resultat
                                                                                   End Function, RegexOptions.IgnoreCase) 'retourne le texte trouvé par la regex dans lequel on change mot par remplacement en ingorant la casse, ne marche plus avec stirng.replace nécessite une autre regex


            Dim IgnoreLaCasse As New Regex(mot, RegexOptions.IgnoreCase) 'le patern dit que l'on cherche le mot entier ou pas en ignorant la case
            Dim texte3 As String = IgnoreLaCasse.Replace(texte, remplacement)


            File.WriteAllText(fichier.Replace(".txt", "-1.txt"), "Mot entier, respecte la casse." & Environment.NewLine & texte1)
            File.WriteAllText(fichier.Replace(".txt", "-2.txt"), "Mot entier, ignore la casse." & Environment.NewLine & texte2)
            File.WriteAllText(fichier.Replace(".txt", "-3.txt"), "Mot ignore la casse." & Environment.NewLine & texte3)
            File.WriteAllText(fichier.Replace(".txt", "-4.txt"), "Mot respecte la casse." & Environment.NewLine & texte.Replace(mot, remplacement)) 'ici on utilise string.Replace qui remplace toute occurence du texte exact


        Next fichier


		End Sub


Avec ce fichier de test
coucou coucou coucou
coucou, Coucou.
1coucou deux coucous trois COUcous 4COUcous


Ce qui ne marche pas bien, c'est pour la recherche de mot entier, le 2eme coucou de la 1ere ligne passe à la trappe.
C'est dû au fait que la première occurrence est "coucou ", du coup la recherche reprend à "coucou".
Si je lui demande de chercher un début, un caractère non alphanumérique ou rien, il prendra toutes les occurrences, mêmes les mot pas entiers...
L'astuce consiste à faire 2 passes.
Messages postés
18038
Date d'inscription
lundi 7 décembre 2009
Statut
Modérateur
Dernière intervention
11 avril 2018
232
Bonjour, Whismeril,
Il suffit peut-être d'ajouter un espace avant et après la chaîne à traiter avant de la traiter. Quitte à "trimer" ensuite la chaîne après traitement.
Messages postés
15172
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
1 décembre 2020
465 >
Messages postés
18038
Date d'inscription
lundi 7 décembre 2009
Statut
Modérateur
Dernière intervention
11 avril 2018

Ha oui, j'essayerai ça tout à l' heure.
Comme je l'ai dit plus haut, c'est vite fait.

Bonjour au fait!
Messages postés
15172
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
1 décembre 2020
465 >
Messages postés
15172
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
1 décembre 2020

Bon ça n'est pas si simple qu'il n'y parait. Si je remplace tous les espaces par des doubles espaces et qu'à la fin je fait l'inverse, cela fonctionne pour mon exemple.
Mais si dans mon fichier j'ai ceci
coucou coucou.Toto() coucou
le deuxième coucou passe toujours à la trappe.
Il faut alors une regex qui va remplacer tous les signe de ponctuation par ce signe et un espace, puis à la fin remplacer les signes de ponctuations suivis d'un espace par le signe seul.
Cela 5 opérations de remplacement, que l'on peut réduire à 3 en combinant la ponctuation avec les doubles espaces.
Au final, faire 2 passes de la première solution est moins couteux en temps d'exécution et d'écriture de code.
Messages postés
18038
Date d'inscription
lundi 7 décembre 2009
Statut
Modérateur
Dernière intervention
11 avril 2018
232
J'ignore comment on pourrait faire la même chose sous VB.Net.
Sous VB6, j'éclaterais le texte avec strconv et chr(0), puis bouclerais sur l'array obtenu et avec un select case ==>>
Case ",",".",";",":","!","?", je transformerais en " " ==>> un join ==>> un split sur "coucou"
Messages postés
82
Date d'inscription
jeudi 26 décembre 2013
Statut
Membre
Dernière intervention
14 juin 2016

Merci de vous arracher les cheveux pour moi, mais ça ira, je crois être sur la bonne voie ;)

Bonne journée !
Messages postés
18038
Date d'inscription
lundi 7 décembre 2009
Statut
Modérateur
Dernière intervention
11 avril 2018
232
Après réflexion complémentaire, voilà ce que je ferais finalement avec VB6 :
toto = "coucou, aaaa coucou  bbbb. coucoucc iiii coucou! aaaa coucou"
tata = Split(" " & toto & " ", "coucou")
For i = 0 To UBound(tata) - 1
Select Case Left(tata(i + 1), 1)
Case ",", " ", "!"
If Right(tata(i), 1) = " " Then
tata(i + 1) = "#" & tata(i + 1)
End If
End Select
Next
toto = Trim(Replace(Join(tata, "coucou"), "coucou#", "voila"))
MsgBox toto

Messages postés
15172
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
1 décembre 2020
465
L'as tu testé car je pense avoir bien traduit et ça ne marche pas trop bien.

@Drarig, ne t'inquiète pas de nos cheveux, on n'aime bien s'amuser avec certains défis!
Messages postés
18038
Date d'inscription
lundi 7 décembre 2009
Statut
Modérateur
Dernière intervention
11 avril 2018
232
Oui : testé ===>> sans faille sous VB6
Il est possible que tu n'aies pas complété les possibilités ===>> je le fais ===>>
Case ",", " ", "!",":",";","?"
==>>transforme en "voila" tous les mots "coucou" entiers (donc suivis d'un espace, d'une virgule, etc ...) et ne transforme pas ceux qui ne sont pas "entiers" (suivis d'un autre signe que d'une ponctuation ou d'un espace.
Exemple :
toto = "coucou, aaaa coucou bbbb. coucoucc iiii coucou! cccc coucou aaaa coucou"
===>>
voila, aaaa voila bbbb. coucoucc iiii voila! cccc voila aaaa voila

coucoucc n'est pas, lui, transformé, puisque coucou n'y est pas un mot "distinct" (suivi de "cc")

EDIT : Il se peut également que tu aies un "#" perturbateur dans la chaîne à traiter ===>> on va utiliser chr(1) plutôt que "#" (ce caractère chr(1) ne risque pas, lui, d'être présent dans une chaîne). ===>>
toto = "coucou, aaaa coucou  bbbb. coucoucc iiii coucou! cccc coucou aaaa coucou"
tata = Split(" " & toto & " ", "coucou")
For i = 0 To UBound(tata) - 1
Select Case Left(tata(i + 1), 1)
Case ",", " ", "!", ":", ";", "?"
If Right(tata(i), 1) = " " Then
tata(i + 1) = Chr(1) & tata(i + 1)
End If
End Select
Next
toto = Trim(Replace(Join(tata, "coucou"), "coucou" & Chr(1), "voila"))
msgbox toto
Messages postés
15172
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
1 décembre 2020
465
En fait VB6 est plus permissif que VB.Net
le split donne
""
", aaaa "
"# bbbb. "
"cc iiii "
"#! aaaa "
""
La méthode SubString(0,1) plante quand le texte est "" ce qui ne doit pas être le cas de Right ou Left.
Idem pour Caractere = monTexte(0) si monTexte est "", y a pas d'indice 0.

Une fois pris en compte ces "paramètres" voilà une transcription qui marche avec ton exemple
            Dim tab = {"coucou"} 'le mot à trouver car le split marche avec un tableau
            Dim mot As String = "coucou"

            Dim tata As String() = texte.Split(tab, StringSplitOptions.None)'le texte est déjà chargé dans la variable texte de type string
            For i = 0 To tata.Length - 2
                Dim caractereSuivant As String
                If tata(i + 1) <> "" Then 'pour s'affranchir du dernier ""
                    caractereSuivant = tata(i + 1).Substring(0, 1)
                Else
                    caractereSuivant = ""
                End If


                Select Case caractereSuivant

                    Case ",", " ", "!", ":", ";", "?", ""
                        If tata(i) = "" Then 'pour s'affranchir du premier ""
                            tata(i + 1) = "#" & tata(i + 1)
                        Else
                            If tata(i)(tata(i).Length - 1) = " " Then
                                tata(i + 1) = "#" & tata(i + 1)
                            End If
                        End If

                End Select
            Next i


Cependant, dans mon exemple il y a plusieurs lignes et là ça ne marche plus, puisque ce qui suit n'est pas un espace.
Il faut donc tester aussi le retour à la ligne, le fin de ligne et le fin de texte, ça se complique un peu.
Messages postés
18038
Date d'inscription
lundi 7 décembre 2009
Statut
Modérateur
Dernière intervention
11 avril 2018
232
Les retours à la ligne ne me posent par contre aucune espèce de problème sous VB6. Il me suffit de les inclure dans mon code, ainsi ===>>
toto = "coucou, aaaa coucou  bbbb. coucoucc iiii coucou! " & _
"cccc coucou aaaa coucou" & vbCrLf & "coucou bbb"
tata = Split(" " & toto & " ", "coucou")
For i = 0 To UBound(tata) - 1
Select Case Left(tata(i + 1), 1)
Case ",", " ", "!", ":", ";", "?", Chr(10), Chr(13) '===>> ICI
Select Case Right(tata(i), 1)
Case " ", Chr(10), Chr(13) ' ====>>> ET LA
tata(i + 1) = Chr(1) & tata(i + 1)
End Select
End Select
Next
toto = Trim(Replace(Join(tata, "coucou"), "coucou" & Chr(1), "voila"))
MsgBox toto


Obtenu :
voila, aaaa voila bbbb. coucoucc iiii voila! cccc voila aaaa voila
voila bbb


________________________
Réponse exacte ? => "REPONSE ACCEPTEE" facilitera les recherches.
Pas d'aide en ligne installée ? => ne comptez pas sur moi pour simplement répéter son contenu. Je n'interviend
Messages postés
15172
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
1 décembre 2020
465
Bonjour, c'est tout à fait gérable en VB.net aussi, mais ça devient une usine à gaz au regard d'une fonction implémentée existante.
Je vais voir si je peux forcer la regex d'une façon ou d'une autre à prendre deux mots entiers à la suite.
Messages postés
15172
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
1 décembre 2020
465
Me revoilà avec deux codes, dans les deux cas, j'initie au début si on cherche par mot entier et si on respecte la casse.

Le premiers reprend ce que j'avais fait hier, tout en regex avec la double passe
    Private Sub FindReplaceDeuxPasses()
        Dim path As String = Application.StartupPath & "\test"
        Dim txtFiles() As String = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories) 'répère le nom des fichiers dans le répertoire et sous les répertoires
        Dim mot As String = "coucou" 'le mot à trouver
        Dim remplacement As String = "Remplacé"
        Dim monOption As RegexOptions = RegexOptions.None 'ici on choisi entre None et IgnoreCase
        Dim motEntier As Boolean = True 'ici on choisit mot entier ou pas

        For Each fichier As String In txtFiles
            Dim texte As String = File.ReadAllText(fichier)
            Dim nouveauTexte As String

            If motEntier Then
                nouveauTexte = Regex.Replace(texte, "(^|\W)" & mot & "($|\W)", Function(match) match.Value.Replace(mot, remplacement), monOption) 'retourne le texte trouvé par la regex dans lequel on change mot par remplacement
                nouveauTexte = Regex.Replace(nouveauTexte, "(^|\W)" & mot & "($|\W)", Function(match) match.Value.Replace(mot, remplacement), monOption) 'retourne le texte trouvé par la regex dans lequel on change mot par remplacement
            Else
                nouveauTexte = Regex.Replace(texte, mot, remplacement, monOption)
            End If

            File.WriteAllText(fichier.Replace(".txt", "-1.txt"), nouveauTexte)
        Next fichier


    End Sub


Le second est une variante de l'idée d'Ucfoutu avec le split.
Je me sers d'une regex pour chercher toutes les occurrences du mot (pas entier), puis je teste par regex le texte immédiatement autour et recompose le texte en fonction. Je n'ai pas jugé utile de faire un string.Join() à la fin comme le code d'Uc, car j'ai déjà une boucle qui tourne et donc la reconstitution du texte ne perd pas trop de temps.
    Private Sub FindReplaceAvecSplit()
        Dim path As String = Application.StartupPath & "\test"
        Dim txtFiles() As String = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories) 'répère le nom des fichiers dans le répertoire et sous les répertoires
        Dim mot As String = "coucou" 'le mot à trouver
        Dim remplacement As String = "Remplacé"
        Dim monOption As RegexOptions = RegexOptions.None 'ici on choisi entre None et IgnoreCase
        Dim motEntier As Boolean = True 'ici on choisit mot entier ou pas

        For Each fichier As String In txtFiles
            Dim texte As String = File.ReadAllText(fichier)
            Dim nouveauTexte As String

            If motEntier Then
                Dim maRegex As New Regex(mot, monOption) 'parttern qui recherche juste le mot
                Dim matches As MatchCollection = maRegex.Matches(texte) ' retourne la collection des occurences
                Dim split() As String = texte.Split({mot}, StringSplitOptions.None)
                Dim indexSplit As Integer = 0
                nouveauTexte = split(indexSplit)
                indexSplit += 1

                For Each m As Match In matches
                    'extrait de texte
                    Dim index As Integer = If(m.Index > 0, m.Index - 1, 0) 'on prend un caractère avant l'occurence si c'est possible
                    Dim longueur As Integer = If(m.Index + m.Length < texte.Length, m.Length + 2, texte.Length - index) 'l'extrait de texte ne doit pas depasser le texte

                    Dim extrait As String = texte.Substring(index, longueur)
                    Dim pattern As String = String.Format("(\W{0}\W)|(^{0}\W)|(\W{0}$)|(^{0}$)", mot)
                    If Regex.IsMatch(extrait, pattern) Then
                        nouveauTexte &= remplacement
                    Else
                        nouveauTexte &= mot
                    End If

                    If indexSplit < split.Length Then
                        nouveauTexte &= split(indexSplit)
                        indexSplit += 1
                    End If
                Next m
            Else
                nouveauTexte = Regex.Replace(texte, mot, remplacement, monOption)
            End If

            File.WriteAllText(fichier.Replace(".txt", "-1.txt"), nouveauTexte)

        Next fichier
    End Sub