web-dev-qa-db-fra.com

Meilleure recherche d'une chaîne dans tous les fichiers à l'aide de C #

Après avoir référé de nombreux blogs et articles, j'ai atteint le code suivant pour rechercher une chaîne dans tous les fichiers d'un dossier. Cela fonctionne bien dans mes tests.

[~ # ~] questions [~ # ~]

  1. Existe-t-il une approche plus rapide pour cela (en utilisant C #)?
  2. Y a-t-il un scénario qui échouera avec ce code?

Remarque: j'ai testé avec de très petits fichiers. Aussi très peu de fichiers.

[~ # ~] code [~ # ~]

static void Main()
    {
        string sourceFolder = @"C:\Test";
        string searchWord = ".class1";

        List<string> allFiles = new List<string>();
        AddFileNamesToList(sourceFolder, allFiles);
        foreach (string fileName in allFiles)
        {
            string contents = File.ReadAllText(fileName);
            if (contents.Contains(searchWord))
            {
                Console.WriteLine(fileName);
            }
        }

        Console.WriteLine(" ");
        System.Console.ReadKey();
    }

    public static void AddFileNamesToList(string sourceDir, List<string> allFiles)
    {

            string[] fileEntries = Directory.GetFiles(sourceDir);
            foreach (string fileName in fileEntries)
            {
                allFiles.Add(fileName);
            }

            //Recursion    
            string[] subdirectoryEntries = Directory.GetDirectories(sourceDir);
            foreach (string item in subdirectoryEntries)
            {
                // Avoid "reparse points"
                if ((File.GetAttributes(item) & FileAttributes.ReparsePoint) != FileAttributes.ReparsePoint)
                {
                    AddFileNamesToList(item, allFiles);
                }
            }

    }

[~ # ~] référence [~ # ~]

  1. tilisation de StreamReader pour vérifier si un fichier contient une chaîne
  2. Fractionner une chaîne avec deux critères
  3. C # détecte les jonctions de dossier dans un chemin
  4. Détecter les liens symboliques, les points de jonction, les points de montage et les liens durs
  5. FolderBrowserDialog SelectedPath avec points d'analyse
  6. C # - Conversion de tableaux d'octets de haute qualité
28
Lijo

Au lieu de File.ReadAllText () mieux utiliser

File.ReadLines(@"C:\file.txt");

Il retourne IEnumerable (cédé) donc vous n'aurez pas à lire le fichier entier si votre chaîne est trouvée avant que la dernière ligne du fichier texte soit atteinte

26
VladL

J'ai écrit quelque chose de très similaire, quelques changements que je recommanderais.

  1. Utilisez Directory.EnumerateDirectories au lieu de GetDirectories, il retourne immédiatement avec un IEnumerable afin que vous n'ayez pas besoin d'attendre qu'il ait fini de lire tous les répertoires avant le traitement.
  2. Utilisez ReadLines au lieu de ReadAllText, cela ne chargera qu'une ligne à la fois en mémoire, ce sera un gros problème si vous frappez un gros fichier.
  3. Si vous utilisez une version suffisamment nouvelle de .Net Use Parallel.ForEach , cela vous permettra de rechercher plusieurs fichiers à la fois.
  4. Vous ne pourrez peut-être pas ouvrir le fichier, vous devez vérifier les autorisations de lecture ou ajouter au manifeste que votre programme nécessite des privilèges administratifs (vous devriez quand même vérifier)

Je créais un outil de recherche binaire, voici quelques extraits de ce que j'ai écrit pour vous aider

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    Parallel.ForEach(Directory.EnumerateFiles(_folder, _filter, SearchOption.AllDirectories), Search);
}

//_array contains the binary pattern I am searching for.
private void Search(string filePath)
{
    if (Contains(filePath, _array))
    {
        //filePath points at a match.
    }
}

private static bool Contains(string path, byte[] search)
{
    //I am doing ReadAllBytes due to the fact that I am doing a binary search not a text search
    //  There are no "Lines" to seperate out on.
    var file = File.ReadAllBytes(path);
    var result = Parallel.For(0, file.Length - search.Length, (i, loopState) =>
        {
            if (file[i] == search[0])
            {
                byte[] localCache = new byte[search.Length];
                Array.Copy(file, i, localCache, 0, search.Length);
                if (Enumerable.SequenceEqual(localCache, search))
                    loopState.Stop();
            }
        });
    return result.IsCompleted == false;
}

Cela utilise deux boucles parallèles imbriquées. Cette conception est terriblement inefficace et pourrait être grandement améliorée en utilisant algorithme de recherche Booyer-Moore mais je n'ai pas pu trouver une implémentation binaire et je n'ai pas eu le temps quand je l'ai écrite à l'origine pour l'implémenter moi-même .

10
Scott Chamberlain

le principal problème ici est que vous recherchez tous les fichiers en temps réel pour chaque recherche. il existe également la possibilité de conflits d'accès aux fichiers si plus de 2 utilisateurs effectuent une recherche en même temps.

pour améliorer considérablement les performances, j'indexerais les fichiers à l'avance, et au fur et à mesure de leur modification/sauvegarde. stocker l'indexé en utilisant quelque chose comme lucene.net puis interroger l'index (à nouveau en utilisant luence.net ) et renvoyer les noms de fichiers à l'utilisateur. afin que l'utilisateur n'interroge jamais directement les fichiers.

si vous suivez les liens dans ce SO Post vous pouvez avoir une longueur d'avance sur la mise en œuvre de l'indexation. Je n'ai pas suivi les liens, mais ça vaut le coup d'oeil.

Attention, ce sera un changement intense par rapport à votre approche actuelle et nécessitera

  1. un service pour surveiller/indexer les fichiers
  2. le projet UI
3
Jason Meckley
  1. Au lieu de Contains mieux utiliser l'algorithme de recherche Boyer-Moore.

  2. Scénario d'échec: le fichier n'a pas l'autorisation de lecture.

1
Serj-Tm

Je pense que votre code échouera avec une exception si vous manquez permission to open a file.

Comparez-le avec le code ici: http://bgrep.codeplex.com/releases/view/36186

Ce dernier code prend en charge

  1. recherche d'expression régulière et
  2. filtres pour les extensions de fichier

- des choses que vous devriez probablement considérer.

1
Brannon