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

Messages postés
82
Date d'inscription
jeudi 26 décembre 2013
Statut
Membre
Dernière intervention
14 juin 2016
- - Dernière réponse : Whismeril
Messages postés
13858
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
23 octobre 2019
- 11 févr. 2015 à 14:01
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
Afficher la suite 

7 réponses

Meilleure réponse
Messages postés
13858
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
23 octobre 2019
312
1
Merci
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.

Dire « Merci » 1

Quelques mots de remerciements seront grandement appréciés. Ajouter un commentaire

Codes Sources 197 internautes nous ont dit merci ce mois-ci

Drarig29
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 ;)
Commenter la réponse de Whismeril
Messages postés
13858
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
23 octobre 2019
312
1
Merci
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.

Dire « Merci » 1

Quelques mots de remerciements seront grandement appréciés. Ajouter un commentaire

Codes Sources 197 internautes nous ont dit merci ce mois-ci

ucfoutu
Messages postés
18039
Date d'inscription
lundi 7 décembre 2009
Statut
Modérateur
Dernière intervention
11 avril 2018
212 -
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.
Whismeril
Messages postés
13858
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
23 octobre 2019
312 > ucfoutu
Messages postés
18039
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!
Whismeril
Messages postés
13858
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
23 octobre 2019
312 > Whismeril
Messages postés
13858
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
23 octobre 2019
-
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.
Commenter la réponse de Whismeril
Messages postés
18039
Date d'inscription
lundi 7 décembre 2009
Statut
Modérateur
Dernière intervention
11 avril 2018
212
1
Merci
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"

Dire « Merci » 1

Quelques mots de remerciements seront grandement appréciés. Ajouter un commentaire

Codes Sources 197 internautes nous ont dit merci ce mois-ci

Commenter la réponse de ucfoutu
Messages postés
82
Date d'inscription
jeudi 26 décembre 2013
Statut
Membre
Dernière intervention
14 juin 2016
0
Merci
Merci de vous arracher les cheveux pour moi, mais ça ira, je crois être sur la bonne voie ;)

Bonne journée !
Commenter la réponse de Drarig29
Messages postés
18039
Date d'inscription
lundi 7 décembre 2009
Statut
Modérateur
Dernière intervention
11 avril 2018
212
0
Merci
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

Whismeril
Messages postés
13858
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
23 octobre 2019
312 -
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!
ucfoutu
Messages postés
18039
Date d'inscription
lundi 7 décembre 2009
Statut
Modérateur
Dernière intervention
11 avril 2018
212 -
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
Whismeril
Messages postés
13858
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
23 octobre 2019
312 -
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.
Commenter la réponse de ucfoutu
Messages postés
18039
Date d'inscription
lundi 7 décembre 2009
Statut
Modérateur
Dernière intervention
11 avril 2018
212
0
Merci
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
Whismeril
Messages postés
13858
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
23 octobre 2019
312 -
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.
Commenter la réponse de ucfoutu
Messages postés
13858
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
23 octobre 2019
312
0
Merci
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



Commenter la réponse de Whismeril