【问题标题】:vba that searches for a string in all the files inside a folder and its subfoldersvba 在文件夹及其子文件夹内的所有文件中搜索字符串
【发布时间】:2016-11-11 17:32:53
【问题描述】:

我有一个庞大的脚本要制作,我已经部分完成(将 xml 文件解析为 vba 并删除某些不需要的孩子),但我有一点被打动了。

我的工作表中的单元格 A1:A1500 中有字符串(从我以前的输出中获得),并且我在放置工作簿的同一路径中有一个名为“model”的文件夹(该文件夹有许多子文件夹和内部子文件夹存在许多 .c 、 .h 、 .xml 文件类型)。

我需要一个脚本,它将获取 A1 中的字符串并在文件夹“模型”及其子文件夹中的所有文件中搜索,如果该字符串存在于任何文件中,我必须打印/放置“找到的字符串”在单元格 B1 中,如果字符串不存在于任何文件中,我必须在单元格 B1 中打印/放置“未找到”。同样,我需要在“模型”文件夹中的所有文件中搜索 A2:A1500 中的所有字符串,并在单元格 B2:B1500 中打印/放置“找到的字符串”/未找到。

以下是我在工作表 A1:A4 列中的一些字符串:

vel_gradient

D_speed_20

AGB_router_1

F10_35_XS

我对 vba 有点熟悉,但不知道如何实现。

接受有关脚本的任何帮助。有人可以帮我解决这个问题吗?

【问题讨论】:

  • 在此页面右侧的“相关”标题下,您将找到在文件夹/子文件夹中搜索文件的示例。如果遇到问题,请尝试其中一种并用代码发回。
  • 搜索文件夹和子文件夹需要递归,这需要一点时间来解决。在文件内容中搜索字符串意味着将文本加载到内存中,因此我将其设置为仅打开每个文件一次并同时查找所有字符串以加快性能。
  • 我用谷歌搜索了一些可能性,而且在这里,我找到了某些示例来搜索文件夹/子文件夹中带有文件名的字符串,但我没有找到任何在文件夹中的所有文件中搜索字符串的东西/子文件夹使用 VBA。这就是为什么我问了一个问题,否则我会关注并修改@Tim Williams 的一些帖子
  • 本网站旨在帮助您编写您正在尝试使用的代码:通常要求完整答案的问题不会得到很好的回应。如果您自己着手解决问题并取得一些进展,您更有可能获得帮助。例如,哪一部分给你带来了问题?找到所有文件?在文件中搜索特定的文本?还有什么?
  • 是的,我明白了你的逻辑,实际上性能对我来说并不重要,即使我的脚本需要 3-5 分钟才能完成并向我显示结果很好。你能帮我写代码吗,因为我不确定如何实现它@Portland Runner

标签: vba excel


【解决方案1】:

正如问题cmets中所指出的,这个问题的答案涉及递归,这意味着一个或多个子例程或函数一次又一次地调用自己,等等。幸运的是,Excel会为您跟踪所有这些.我的解决方案还利用了一个 Excel 技巧,该技巧允许您创建或卸载数组,而无需使用 Range.Value 属性进行迭代。还包括一个字符串缩进变量,以帮助可视化递归是如何发生的。只需在不再需要时将 Debug.Print 语句注释掉即可。

解决方案涉及 3 个步骤。

  1. 创建一个包含所有可以匹配的字符串的数组以及 2 个并行数组来保存找到/未找到的字符串以及匹配该字符串的第一个文件

  2. 将 3 个数组 ByRef 传递给处理给定文件夹的所有子文件夹和文件的子例程。任何子文件夹递归回文件夹子例程,而文件由单独的文件例程处理。

  3. 处理完所有子文件夹和文件后,从关联的数组中填充找到/未找到的列。

享受

第 1 步 - 主要方法

' The main sub routine.
Public Sub FindStrings(strFolder As String, Optional wksSheet As Worksheet = Nothing)
' Used examples given, better to convert to variables and calculate at run time.
Const lngFirstRow As Long = 1
Const lngLasstRow As Long = 1500
Const strStringsCol As String = "A"
Const strMatchesFoundCol As String = "B"
Const strFileNamesCol As String = "C"

Dim lngIndex As Long, lngFolderCount As Long, lngFileCount As Long
Dim strIndent As String
Dim varStrings As Variant, varMatchesFound As Variant, varFileNames As Variant

    If wksSheet Is Nothing Then
        Set wksSheet = ActiveSheet
    End If

    With wksSheet
        ' Create the strings array from the given range value.
        varStrings = .Range(.Cells(lngFirstRow, strStringsCol), .Cells(lngLasstRow, strStringsCol)).Value
        ' Transpose the strings array into a one dimentional array.
        varStrings = Application.WorksheetFunction.Transpose(varStrings)
    End With

    ' Initialize file names array to empty strings.
    ReDim varFileNames(LBound(varStrings) To UBound(varStrings))
    For lngIndex = LBound(varFileNames) To UBound(varFileNames)
        varFileNames(lngIndex) = vbNullString
    Next

    ' Initialize matches found array to empty strings.
    ReDim varMatchesFound(LBound(varStrings) To UBound(varStrings))
    For lngIndex = LBound(varMatchesFound) To UBound(varMatchesFound)
        varMatchesFound(lngIndex) = vbNullString
    Next

    ' Process the main folder.
    Call ProcessFolder(strFolder, strIndent, varStrings, varMatchesFound, varFileNames, lngFolderCount, lngFileCount)

    ' Finish setting up matches found array.
    For lngIndex = LBound(varMatchesFound) To UBound(varMatchesFound)
        If Len(Trim$(varMatchesFound(lngIndex))) = 0 Then
            varMatchesFound(lngIndex) = "Not found"
        End If
    Next

    ' Transpose the associated arrays so we can use them to load found / not found and file names columns.
    varFileNames = Application.WorksheetFunction.Transpose(varFileNames)
    varMatchesFound = Application.WorksheetFunction.Transpose(varMatchesFound)

    ' Set up the found / not found column data from the matches found array.
    With wksSheet
        .Range(.Cells(lngFirstRow, strFileNamesCol), .Cells(lngLasstRow, strFileNamesCol)).Value = varFileNames
        .Range(.Cells(lngFirstRow, strMatchesFoundCol), .Cells(lngLasstRow, strMatchesFoundCol)).Value = varMatchesFound
    End With

    Debug.Print "Folders: "; lngFolderCount, "Files: "; lngFileCount
End Sub

第 2 步 - 进程子文件夹方法

Private Sub ProcessFolder(strFolder As String, ByRef strIndent As String, ByRef varStrings As Variant, ByRef varMatchesFound As Variant, ByRef varFileNames As Variant, ByRef lngFolderCount As Long, lngFileCount As Long)
Dim objFileSystemObject As Object, objFolder As Object, objFile As Object

    ' Use late binding throughout this method to avoid having to set any references.
    Set objFileSystemObject = CreateObject("Scripting.FileSystemObject")
    lngFolderCount = lngFolderCount + 1
    Debug.Print strIndent & "Dir: " & Format(lngFolderCount, "###,##0 ") & strFolder

    For Each objFolder In objFileSystemObject.GetFolder(strFolder).SubFolders
        If objFolder.Name = "history" Then
            'Do Nothing
        Else
            ' Recurse with the current sub folder.
            Call ProcessFolder(objFolder.Path, strIndent & "    ", varStrings, varMatchesFound, varFileNames, lngFolderCount, lngFileCount)
        End If
    Next

    ' Process any files found in the current folder.
    For Each objFile In objFileSystemObject.GetFolder(strFolder).Files
        Call ProcessFile(objFile.Path, strIndent & "    ", varStrings, varMatchesFound, varFileNames, lngFileCount)
    Next

    Set objFileSystemObject = Nothing: Set objFolder = Nothing: Set objFile = Nothing
End Sub

第 3 步 - 进程文件方法

Private Sub ProcessFile(strFullPath As String, ByRef strIndent As String, ByRef varStrings As Variant, ByRef varMatchesFound As Variant, ByRef varFileNames As Variant, ByRef lngFileCount As Long)
On Error Resume Next
Dim objFileSystemObject As Object
Dim strFileContent As String
Dim lngIndex As Long
    lngFileCount = lngFileCount + 1
    Debug.Print strIndent & "File: " & Format(lngFileCount, "###,##0 ") & strFullPath

    ' Use late binding throughout this method to avoid having to set any references.
    Set objFileSystemObject = CreateObject("Scripting.FileSystemObject")
    strFileContent = objFileSystemObject.OpenTextFile(strFullPath).Readall()
    If Err.Number = 0 Then
        ' Check for matched strings by iterating over the strings array.
        For lngIndex = LBound(varStrings) To UBound(varStrings)
            ' Skip zero length strings.
            If Len(Trim$(varStrings(lngIndex))) > 0 Then
                ' We have a matched string.
                If InStr(1, strFileContent, varStrings(lngIndex), vbTextCompare) > 0 Then
                    ' Set up parallel arrays the first time the string is matched.
                    If Len(Trim$(varMatchesFound(lngIndex))) = 0 Then
                        ' Set corresponding array value.
                        varMatchesFound(lngIndex) = "String found"
                        ' Save file name where first match was found.
                        varFileNames(lngIndex) = strFullPath
                    End If
                End If
            End If
        Next
    Else
        Err.Clear
    End If
    Set objFileSystemObject = Nothing
On Error GoTo 0
End Sub

【讨论】:

  • 我正在从另一个子 liKE Call FindStrings (strFolder, Nothing) 调用子 FindStrings,但它会抛出一个错误,提示 Sub or Function not defined 并在 FindStrings () 处停止执行并且它还突出显示Call ProcessFolder
  • 我需要在另一个子的末尾调用/运行FindStrings,我将文件夹路径作为字符串传递,或者如果它提示用户选择文件夹会更好。 @j2associates
  • 我尝试将 FindStrings 放在标准模块中,将 ProcessFile&PorcessFolder 放在类模块中,但它也没有工作。抛出了同样的错误。你能帮忙@j2associates
  • @S6633d:所有三个子例程都应该包含在同一个标​​准模块中。如果您希望它们位于单独的模块中,请将它们全部更改为 Public。此外,在 VBA 编辑器中,单击“调试”菜单,然后选择“编译 VBAProject”。这将突出显示您可能遇到的任何编译错误,以便您修复它们。
  • 只是稍微调整了代码,以便 Debug.Print 语句能够正确显示递归结果。代码的基本功能没有改变。
【解决方案2】:

如果您的文件不是太大,您可以一口气阅读所有内容:

Sub Tester()

    Debug.Print StringInFile("C:\_Stuff\test\File_Val2.txt", "xxx")

End Sub


Function StringInFile(fPath, txtSearch) As Boolean
    StringInFile = InStr(CreateObject("scripting.filesystemobject").opentextfile( _
                         fPath).Readall(), txtSearch) > 0
End Function

但是,如果您需要测试多个字符串,则读取文件一次然后使用 instr() 检查每个字符串会更有效

【讨论】:

    猜你喜欢
    • 2017-04-06
    • 2015-08-04
    • 1970-01-01
    • 1970-01-01
    • 2011-03-30
    • 1970-01-01
    • 2013-02-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多