2016-12-07 3 views
0

J'ai écrit ceci dans VB.NET, mais je suis aussi à l'aise avec C#. J'ai une liste de fichiers que je veux trouver sur un système de fichiers Windows. Basé sur le nom de fichier, je devrai regarder dans un répertoire différent. La liste des fichiers que j'ai est une liste que j'ai compilée au début du programme (qui fonctionne) et elle est stockée dans un DataTable qui n'est pas trié. Voici mon approche.Est-il possible de réduire le nombre de numéros de fichiers?

DataTable Liste des fichiers (cela peut varier de jour en jour, parfois dans les 1,000s +)

- a_111.txt 
- a_222.txt 
- b_333.txt 
- a_444.txt 
- c_555.txt 
- b_666.txt 

répertoires à regarder en fonction du nom de fichier

C:\a\ -- for files begin with a (variable name is A_folder) 
C:\b\ -- for files begin with b (variable name is B_folder) 
C:\c\ -- for files begin with c (variable name is C_folder) 

code:

If DataTableofFiles IsNot Nothing AndAlso DataTableofFiles.Rows.Count > 0 Then 
    For Each row as DataRow In DataTableofFiles.Rows 
    If row("FILENAME").ToString.StartsWith("a") Then 
     Dim a_WriteResultstoA as String = "a.csv" 
     functionfindfiles(A_folder, row("FILENAME").ToString, a_WriteResultstoA) 
    ElseIf row("FILENAME").ToString.StartsWith("b") Then 
     Dim b_WriteResultstoB as String = "b.csv" 
     functionfindfiles(B_folder, row("FILENAME").ToString, b_WriteResultstoB) 
    ElseIf row("FILENAME").ToString.StartsWith("C") Then 
     Dim c_WriteResultstoC as String = "c.csv" 
     functionfindfiles(C_folder, row("FILENAME").ToString, c_WriteResultstoC) 
    End If 
    Next 
End If 

Private Sub functionfindfiles(sourcefolder As String, filename as String, writetofile As String) 
     Try 
      For Each f As String In Directory.EnumerateFiles(sourcefolder, "*.*", SearchOption.AllDirectories) '<-- file enumeration 
        If Path.GetFileName(f).Equals(filename, StringComparison.OrdinalIgnoreCase) Then 
         Using fs As New FileStream(writetofile, FileMode.Append, FileAccess.Write, FileShare.Write) 
          Using sw As StreamWriter = New StreamWriter(fs) 
           If Not New FileInfo(writetofile).Length > 0 Then 
            For i As Integer = 0 To DataTableofFiles.Columns.Count - 1 Step 1 
             sw.Write(DataTableofFiles.Columns(i).ToString) 

             If i < DataTableofFiles.Columns.Count - 1 Then 
              sw.Write(",") 
             End If 
            Next 

            sw.WriteLine() 
           End If 

           For Each row As DataRow In DataTableofFiles.Rows 
            If row("FILENAME").ToString = filename Then 
             For i As Integer = 0 To DataTableofFiles.Columns.Count - 1 Step 1 
              If Not Convert.IsDBNull(row(i)) Then 
               sw.Write(row(i).ToString.Replace(vbLf, "").Replace(",", ";")) 
              End If 

              If i < DataTableofFiles.Columns.Count - 1 Then 
               sw.Write(",") 
              End If 
             Next 

             sw.WriteLine() 
            End If 
           Next 
          End Using 
         End Using 
        Else 
         'write results that are not found here to a file 
        End If 
      Next 
     Catch ex As Exception 
    MessageBox.Show(ex.Message) 
     End Try 
End Sub 

Dans ce cas, l'énumération sur le système de fichiers aura lieu 6 fois. L'exécution peut prendre beaucoup de temps si j'ai beaucoup de fichiers dans les répertoires. Y a-t-il une meilleure approche qui réduira le nombre d'énumérations de fichiers? Ou d'autres zones du code qui peuvent être améliorées pour réduire les opérations supplémentaires effectuées plus que nécessaire? Tout conseil est grandement appréciée. Merci!

+0

Inversez votre foreach. En ce moment vous faites 'foreach (nomFichier dans la liste) {foreach (fichier dans EnumerateFiles) {}}', mais vous pouvez simplement les retourner à 'foreach (fichier dans EnumerateFiles) {foreach (nomFichier dans la liste) {}}'. Vous devrez changer l'architecture de ce que font vos méthodes et comment vous les appelez, mais logiquement le double foreach est tout ce que vous faites et si le code était tout en ligne, vous pourriez inverser trivialement l'ordre des 'foreach' à énumérer juste une fois. – Quantic

+0

Merci pour la réponse. Si j'ai inversé la commande, je ne saurai pas quel "chemin de dossier" passer pour le ForEach à rechercher. – Jayarikahs

Répondre

1

Vous n'allez pas énumérer 6 fois dans votre exemple, vous énumérez A de dossier 3 fois, le dossier B 2 fois et le dossier C 1 fois. Pour réduire ces énumérations supplémentaires, vous pouvez pré-traiter la table de données pour créer des listes de noms de fichiers pour chaque dossier, puis modifier votre méthode pour travailler sur une liste de noms de fichiers au lieu d'un nom de fichier unique. Je n'écris pas en VB alors voici une réponse qui mashes en code C# (désolé je ne pouvais pas insérer mes idées dans un commentaire, c'est une mauvaise réponse car elle ne compile pas).

Notez que tout ce que je l'ai fait à votre méthode a été ajouter à foreach (var filename in listOfFileNames) et a changé la signature d'accepter un List<string> listOfFileNames au lieu de simplement string filename, et l'appelant construit maintenant des listes et se termine le datatable foreach complètement avant d'appeler la méthode une fois pour chaque dossier.

If DataTableofFiles IsNot Nothing AndAlso DataTableofFiles.Rows.Count > 0 Then 

    List<string> allAFileNames = new List<string>(); 
    List<string> allBFileNames = new List<string>(); 
    List<string> allCFileNames = new List<string>(); 

    For Each row as DataRow In DataTableofFiles.Rows 
    If row("FILENAME").ToString.StartsWith("a") Then 
     Dim a_WriteResultstoA as String = "a.csv" 

     allAFileNames.Add(row("FILENAME")); 

    ElseIf row("FILENAME").ToString.StartsWith("b") Then 
     Dim b_WriteResultstoB as String = "b.csv" 

     allBFileNames.Add(row("FILENAME")); 

    ElseIf row("FILENAME").ToString.StartsWith("C") Then 
     Dim c_WriteResultstoC as String = "c.csv" 

     allCFileNames.Add(row("FILENAME")); 

    End If 
    Next 

    if (allAFileNames.Count > 0) 
    { 
     functionfindfiles(A_folder, allAFileNames, a_WriteResultstoA); 
    } 

    if (allBFileNames.Count > 0) 
    { 
     functionfindfiles(B_folder, allBFileNames, b_WriteResultstoB) 
    } 

    if (allAFileNames.Count > 0) 
    { 
     functionfindfiles(C_folder, allCFileNames, c_WriteResultstoC) 
    } 

End If 

Private Sub functionfindfiles(sourcefolder As String, List<string> listOfFileNames, writetofile As String) 
     Try 
      For Each f As String In Directory.EnumerateFiles(sourcefolder, "*.*", SearchOption.AllDirectories) '<-- file enumeration 

        foreach (var filename in listOfFileNames) 
        { 

        If Path.GetFileName(f).Equals(filename, StringComparison.OrdinalIgnoreCase) Then 
         Using fs As New FileStream(writetofile, FileMode.Append, FileAccess.Write, FileShare.Write) 
          Using sw As StreamWriter = New StreamWriter(fs) 
           If Not New FileInfo(writetofile).Length > 0 Then 
            For i As Integer = 0 To DataTableofFiles.Columns.Count - 1 Step 1 
             sw.Write(DataTableofFiles.Columns(i).ToString) 

             If i < DataTableofFiles.Columns.Count - 1 Then 
              sw.Write(",") 
             End If 
            Next 

            sw.WriteLine() 
           End If 

           For Each row As DataRow In DataTableofFiles.Rows 
            If row("FILENAME").ToString = filename Then 
             For i As Integer = 0 To DataTableofFiles.Columns.Count - 1 Step 1 
              If Not Convert.IsDBNull(row(i)) Then 
               sw.Write(row(i).ToString.Replace(vbLf, "").Replace(",", ";")) 
              End If 

              If i < DataTableofFiles.Columns.Count - 1 Then 
               sw.Write(",") 
              End If 
             Next 

             sw.WriteLine() 
            End If 
           Next 
          End Using 
         End Using 
        Else 
         'write results that are not found here to a file 
        End If 
        } 
      Next 
     Catch ex As Exception 
    MessageBox.Show(ex.Message) 
     End Try 
End Sub 
+0

Je vois ce que vous voulez dire maintenant. Il est plus logique de compiler d'abord les listes en fonction des noms de fichiers, puis de les transmettre en une fois. En option, parce que je voulais trouver un fichier spécifique dans un dossier spécifique, je l'ai réécrit pour utiliser FileInfo (nom de fichier) .Exists. Je vais tester votre code et verra comment les deux options fonctionnent contre 1000 fichiers de fichiers. Merci encore pour votre aide. – Jayarikahs

+0

Si vous ne réutilisez pas le 'FileInfo' alors il peut être plus performant de simplement faire un' File.Exists() 'car il n'a pas à construire une vue complète du fichier FileInfo. Aussi je ne connais pas grand-chose sur les tables de données mais si 'row (" FILENAME ")' fait une recherche réelle et si vous savez que les lignes ne changent pas, il devrait être plus performant pour stocker la valeur et rappeler la valeur stockée : 'string thisFilename = row (" FILENAME ")'. Par exemple, 'row (" FILENAME ")' peut prendre 2 secondes pour s'exécuter à chaque fois, mais rappeler la variable stockée est fondamentalement libre. – Quantic

+0

J'ai testé à la fois FileInfo() .Exists et File.Exists() et les résultats sont très similaires. Parfois FileInfo était plus rapide et d'autres fois le fichier était plus rapide, mais avec chaque essai, il n'était que d'une seconde. J'ai testé contre 1 000 enregistrements de données.Peut-être que si le volume était beaucoup plus grand, le résultat peut différer. Merci encore pour votre aide! – Jayarikahs