vba that searches for a string in all the files inside a folder and its subfolders

Deadly 提交于 2020-06-28 05:07:30

问题


I have a huge script to make which I had partly completed (parsing xml file to vba and deleting certain unwanted childs),but i am struck at one point.

I have strings (that are obtained from my previous output) in cells A1:A1500 in my worksheet and I have a folder named "model" in the same path where my workbook is placed (the folder has many subfolders and inside subfolders many .c , .h , .xml file types are present).

I need a script that will take the string in A1 and search inside all the files in the folder "model" and its subfolders and if the string is present in any of the files I have to print/put "string found" in cell B1 and if the string is not present in any of the files I have to print/put "Not found" in cell B1. In the same way I need to search all the strings from A2:A1500 inside all the files in the folder "model" and print/put "string found"/not found in the cells B2:B1500.

Below are some of the strings I have in my worksheet in column A1:A4:

vel_gradient

D_speed_20

AGB_router_1

F10_35_XS

I am somewhat familiar with vba but I am not sure how to implement this.

Any help regarding the script is accepted. Can someone help me with this.


回答1:


As noted in the question comments, the answer to this question involves recursion, which means that one or more sub routines or functions call themselves again and again and again, etc. Fortunately, Excel will keep track of all of this for you. My solution also takes advantage of an Excel trick that allows you to create or unload arrays without iterating by using the Range.Value property. Also included is a string indent variable to help visualize how the recursion is happening. Just comment out the Debug.Print statements when no longer needed.

The solution involves 3 steps.

  1. Create an array of all of the strings which could be matched along with 2 parallel arrays to hold the found / not found strings and the first file where the string was matched

  2. Pass the 3 arrays ByRef to a sub routine that processes all of the sub folders and files for a given folder. Any sub folders recurse back into the folder sub routine, while files are processed by a separate file routine.

  3. After all sub folders and files have been processed, the found / not found column is populated from the associated array.

Enjoy

Step 1 - The main method

' 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

Step 2 - The process sub folder method

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

Step 3 - The process file method

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



回答2:


If your files are not too large you can read all the content in one shot:

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

However if you need to test for multiple strings it would be more efficient to read the file once and then check for each string using instr()



来源:https://stackoverflow.com/questions/40553281/vba-that-searches-for-a-string-in-all-the-files-inside-a-folder-and-its-subfolde

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!