2016-12-07 14 views
0

Я написал это в VB.NET, но мне тоже нравится C#. У меня есть список файлов, которые я хочу найти в файловой системе Windows. Основываясь на имени файла, мне нужно будет посмотреть в другом каталоге. Список файлов, которые у меня есть, - это список, который я скомпилировал в начале программы (который работает), и он хранится в DataTable, который не сортируется. Вот мой подход.Можно ли уменьшить число нумерации файлов?

DataTable Список файлов (это может меняться изо дня в день, иногда в 1,000s +)

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

каталогов заглянуть в основе имени файла

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) 

Код:

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 

В этом случае перечисление в файловой системе произойдет 6 раз. Выполнение может занять очень много времени, если у меня много файлов в каталогах. Есть ли лучший подход, который уменьшит количество перечислений файлов? Или другие области кода, которые могут быть улучшены для уменьшения дополнительных операций, выполняемых больше, чем необходимо? Любые советы высоко ценится. Благодаря!

+0

Обратный ваш foreach's. Прямо сейчас вы делаете 'foreach (имя_файла в списке) {foreach (файл в EnumerateFiles) {}}', но вы можете просто отменить их на 'foreach (файл в EnumerateFiles) {foreach (имя_файла в списке) {}}'. Вам нужно будет изменить архитектуру того, что делают ваши методы, и как вы их называете, но логически двойное foreach - это все, что вы делаете, и если бы весь код был встроен, вы могли бы тривиально изменить порядок 'foreach' для перечисления только один раз. – Quantic

+0

Спасибо за ответ. Если я отменил заказ, я не буду знать, что «путь к папке» должен пройти для поиска в ForEach. – Jayarikahs

ответ

1

Вы не перечисляете 6 раз в своем примере, вы перечисляете папку A 3 раза, папка B 2 раза и папка C 1 раз. Чтобы уменьшить эти дополнительные перечисления, вы можете предварительно обработать таблицу данных для создания списков имен файлов для каждой папки, а затем изменить свой метод для работы с списком имен файлов, а не с одним именем файла. Я не пишу в VB, поэтому вот ответ, который вымывается в коде C# (извините, я не мог вместить свои идеи в комментарий, это плохой ответ, поскольку он не компилируется).

Обратите внимание, что все, что я сделал, чтобы ваш метод был добавить в foreach (var filename in listOfFileNames) и изменил подпись принять List<string> listOfFileNames вместо просто string filename, и вызывающий абонент в настоящее время строит списки и заканчивает DataTable foreach полностью перед вызовом метода один раз для каждой папки.

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

Я понимаю, что вы имеете в виду сейчас. Имеет смысл сначала скомпилировать списки на основе имен файлов, а затем передать их в 1 раз. При желании, поскольку я хотел найти конкретный файл в определенной папке, я переписал его для использования FileInfo (filename) .Exists. Я проверю ваш код и увижу, как обе опции работают против 1000 файлов. Еще раз спасибо за вашу помощь. – Jayarikahs

+0

Если вы не используете «FileInfo», тогда может быть более просто выполнить «File.Exists()», так как ему не нужно создавать весь файл FileInfo. Кроме того, я мало знаю о таблицах данных, но если 'row (" FILENAME ")' выполняет фактический поиск и если вы знаете, что строки не меняются, то это должно быть более эффективным, чтобы сохранить значение один раз и вызвать сохраненное значение : 'string thisFilename = row (" FILENAME ")'. I.e., 'row (" FILENAME ")' может занять 2 секунды для запуска каждый раз, но вызов сохраненной переменной в основном свободен. – Quantic

+0

Я тестировал как FileInfo(). Exists и File.Exists(), и результаты очень похожи. Иногда FileInfo был быстрее, а в других случаях Файл был быстрее, но с каждым испытанием он был всего на 1 секунду. Я протестировал против 1000 подавать или брать записи.Возможно, если объем был намного больше, результат может отличаться. Еще раз спасибо за вашу помощь! – Jayarikahs