问题
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.
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
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.
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