使用Excel VBA查找工作簿中的所有匹配项

我正在尝试编写一个VBA例程,它将接收一个string,search给定的Excel工作簿,并返回所有可能的匹配项。

我目前有一个工作的实现,但它是非常缓慢的,因为它是一个双循环。 当然,内置的Excel Findfunction是“优化”来find一个匹配,但我希望它返回一个初始匹配数组,然后我可以应用更多的方法。

我会发布一些我已经有的伪代码

 For all sheets in workbook For all used rows in worksheet If cell matches search string do some stuff end end end 

如前所述,这个双循环使事情运行非常缓慢,所以我正在寻求摆脱这个如果可能的话。 有什么build议么?

UPDATE

虽然下面的答案会改善我的方法,但我最终还是采取了稍微不同的做法,因为我需要一遍又一遍地做多个查询。

我决定循环遍历文档中的所有行,并创build一个包含每个唯一行的键的字典。 这个指向的值将会是一个可能的匹配列表,所以当我稍后查询时,我可以简单地检查它是否存在,如果是的话,只需要获得一个匹配的快速列表。

基本上只是做一个初始扫描,将所有内容都存储在可pipe理的结构中,然后查询可以在O(1)时间内完成的结构

使用上面指出的Range.Find方法,以及工作簿中每个工作表的循环,是最快的方法。 例如,以下是查找string“问题?” 在每个工作表中,并用string“Answered!”replace它。

 Sub FindAndExecute() Dim Sh As Worksheet Dim Loc As Range For Each Sh In ThisWorkbook.Worksheets With Sh.UsedRange Set Loc = .Cells.Find(What:="Question?") If Not Loc Is Nothing Then Do Until Loc Is Nothing Loc.Value = "Answered!" Set Loc = .FindNext(Loc) Loop End If End With Set Loc = Nothing Next End Sub 
 Function GetSearchArray(strSearch) Dim strResults As String Dim SHT As Worksheet Dim rFND As Range Dim sFirstAddress For Each SHT In ThisWorkbook.Worksheets Set rFND = Nothing With SHT.UsedRange Set rFND = .Cells.Find(What:=strSearch, LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlRows, SearchDirection:=xlNext, MatchCase:=False) If Not rFND Is Nothing Then sFirstAddress = rFND.Address Do If strResults = vbNullString Then strResults = "Worksheet(" & SHT.Index & ").Range(" & Chr(34) & rFND.Address & Chr(34) & ")" Else strResults = strResults & "|" & "Worksheet(" & SHT.Index & ").Range(" & Chr(34) & rFND.Address & Chr(34) & ")" End If Set rFND = .FindNext(rFND) Loop While Not rFND Is Nothing And rFND.Address <> sFirstAddress End If End With Next If strResults = vbNullString Then GetSearchArray = Null ElseIf InStr(1, strResults, "|", 1) = 0 Then GetSearchArray = Array(strResults) Else GetSearchArray = Split(strResults, "|") End If End Function Sub test2() For Each X In GetSearchArray("1") Debug.Print X Next End Sub 

仔细做一个Find Loop时,你不会陷入无限循环…引用第一个find的单元格地址,并在每个“FindNext”语句之后进行比较,以确保它没有返回到第一个find的单元格。

你可以使用Range.Find方法:

http://msdn.microsoft.com/en-us/library/office/ff839746.aspx

这会让你第一个包含searchstring的单元格。 通过将“After”参数设置为下一个单元格来重复此操作,您将获得所有其他事件,直到您返回首次出现为止。

这可能会快得多。

您可以将数据读取到数组中。 从那里你可以做记忆中的匹配,而不是一次读一个单元格。

将单元格内容传递到VBA数组中

下面的代码避免了创build无限循环。 假设XYZ是我们在工作簿中查找的string。

  Private Sub CommandButton1_Click() Dim Sh As Worksheet, myCounter Dim Loc As Range For Each Sh In ThisWorkbook.Worksheets With Sh.UsedRange Set Loc = .Cells.Find(What:="XYZ") If Not Loc Is Nothing Then MsgBox ("Value is found in " & Sh.Name) myCounter = 1 Set Loc = .FindNext(Loc) End If End With Next If myCounter = 0 Then MsgBox ("Value not present in this worrkbook") End If End Sub 

基于B Hart的答案,下面是我的一个函数版本,该函数在一个范围内search一个值,并返回所有find的范围(单元格):

 Function FindAll(ByVal rng As Range, ByVal searchTxt As String) As Range Dim foundCell As Range Dim firstAddress Dim rResult As Range With rng Set foundCell = .Find(What:=searchTxt, _ After:=.Cells(.Cells.Count), _ LookIn:=xlValues, _ LookAt:=xlWhole, _ SearchOrder:=xlByRows, _ SearchDirection:=xlNext, _ MatchCase:=False) If Not foundCell Is Nothing Then firstAddress = foundCell.Address Do If rResult Is Nothing Then Set rResult = foundCell Else Set rResult = Union(rResult, foundCell) End If Set foundCell = .FindNext(foundCell) Loop While Not foundCell Is Nothing And foundCell.Address <> firstAddress End If End With Set FindAll = rResult End Function 

在整个工作簿中search一个值:

 Dim wSh As Worksheet Dim foundCells As Range For Each wSh In ThisWorkbook.Worksheets Set foundCells = FindAll(wSh.UsedRange, "YourSearchString") If Not foundCells Is Nothing Then Debug.Print ("Results in sheet '" & wSh.Name & "':") Dim cell As Range For Each cell In foundCells Debug.Print ("The value has been found in cell: " & cell.Address) Next End If Next