【问题标题】:Is it possible to reduce number of file numeration?是否可以减少文件编号?
【发布时间】:2016-12-07 22:28:32
【问题描述】:

我是在 VB.NET 中编写的,但我对 C# 也很满意。我有一个要在 Windows 文件系统上查找的文件列表。根据文件名,我需要查看不同的目录。我拥有的文件列表是我在程序开始时编译的列表(有效),它存储在未排序的 DataTable 中。这是我的方法。

DataTable 文件列表(这可能每天都在变化,有时超过 1,000 秒)

- 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 次。如果目录中有很多文件,执行可能需要很长时间。有没有更好的方法可以减少文件枚举的数量?或者代码中可以改进的其他区域以减少执行的额外操作超出需要?任何意见是极大的赞赏。谢谢!

【问题讨论】:

  • 反转你的 foreach 的。现在您可以使用foreach (fileName in list){ foreach (file in EnumerateFiles){ }},但您可以将它们反转为foreach (file in EnumerateFiles) { foreach (fileName in list){ }}。你必须改变你的方法做什么以及你如何调用它们的架构,但从逻辑上讲,双 foreach 是你所做的一切,如果代码都是内联的,你可以简单地颠倒 foreach 的顺序来枚举只有一次。
  • 感谢您的回复。如果我颠倒了顺序,我将不知道要通过什么“文件夹路径”让 ForEach 搜索。

标签: c# vb.net enumeration file-search


【解决方案1】:

在您的示例中,您没有枚举 6 次,而是枚举了文件夹 A 3 次、文件夹 B 2 次和文件夹 C 1 次。为了减少这些额外的枚举,您可以预处理数据表以构建每个文件夹的文件名列表,然后修改您的方法以处理文件名列表而不是单个文件名。我不是用 VB 编写的,所以这是一个用 c# 代码混合的答案(抱歉,我无法将我的想法融入评论,这是一个糟糕的答案,因为它无法编译)。

请注意,我对您的方法所做的只是添加foreach (var filename in listOfFileNames) 并将签名更改为接受List&lt;string&gt; listOfFileNames 而不仅仅是string filename,调用者现在构建列表并在调用之前完全完成数据表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

【讨论】:

  • 我明白你现在的意思了。首先根据文件名编译列表然后在 1 go 中传递它更有意义。可选地,因为我想在特定文件夹中查找特定文件,所以我重写了它以使用 FileInfo(filename).Exists。我将测试您的代码,并查看这两个选项对 1,000 个文件的执行情况。再次感谢您的帮助。
  • 如果您不重用FileInfo,那么只执行File.Exists() 可能会更高效,因为它不必构建文件的整个FileInfo 视图。此外,我对数据表了解不多,但如果row("FILENAME") 进行实际查找并且如果您知道行没有更改,那么存储一次值并调用存储值应该会更高效:string thisFilename = row("FILENAME")。即row("FILENAME")每次运行可能需要2秒,但是调用存储的变量基本上是免费的。
  • 我测试了 FileInfo().Exists 和 File.Exists(),结果非常相似。有时 FileInfo 更快,有时 File 更快,但每次试验只有 1 秒。我针对 1,000 条给予或索取记录进行了测试。也许如果体积更大,结果可能会有所不同。再次感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2019-04-08
  • 2013-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-14
  • 2017-07-04
  • 2021-12-21
相关资源
最近更新 更多