Function 自定义查找函数
我试图创建一个函数,在搜索整个活动工作表后,该函数将返回包含特定字符串的单元格总数。很像Find and Replace中的“x单元格”是如何工作的 到目前为止,我有:Function 自定义查找函数,function,vba,excel,Function,Vba,Excel,我试图创建一个函数,在搜索整个活动工作表后,该函数将返回包含特定字符串的单元格总数。很像Find and Replace中的“x单元格”是如何工作的 到目前为止,我有: Function FINDIST(stringToFind) Dim counter As Integer: counter = 0 For Each Cell In ActiveSheet.UsedRange.Cells If InStr (Cell, stringToFind) > 0 Then counter = c
Function FINDIST(stringToFind)
Dim counter As Integer: counter = 0
For Each Cell In ActiveSheet.UsedRange.Cells
If InStr (Cell, stringToFind) > 0
Then counter = counter + 1
End If
Next
End Function
按照Tony Dallimore的建议使用Find,并将您的返回类型更改为Long MSDN文章:
函数FINDIST(stringToFind)的长度
变暗计数器长度:计数器=0
调光范围
将第一个地址设置为字符串
使用ActiveSheet.UsedRange
集合c=.Find(stringToFind,LookIn:=xlValues,LookAt:=xlPart)
如果不是,那么c什么都不是
firstAddress=c.地址
做
计数器=计数器+1
集合c=.FindNext(c)
循环而不是c为Nothing,c.Address为firstAddress
如果结束
以
FINDIST=计数器
端函数
Find通常比编码的等价物更快,但我还没有针对其他任何东西进行过速度测试,如果它是快的还是慢的,我会对这里感兴趣。另一种方法:
Function FINDIST(stringToFind) As Long
FINDIST = Evaluate("SUM(IFERROR(SEARCH(" & Chr(34) _
& "*" & stringToFind & "*" & Chr(34) & "," _
& ActiveSheet.UsedRange.Address & ",1),0))")
End Function
这将在使用范围内的每个单元格中搜索stringToFind
,如果在单元格中找到该字符串,则返回一个带1的数组,如果未找到该字符串,则返回错误。使用IFERROR
部分将错误转换为零,然后SUM
对生成的二进制数组求和
这将只计算每个单元格中出现的stringToFind
一次,即使它出现不止一次,但查看您的代码,我假设这就是您要查找的内容
我希望有帮助
更新
出于好奇,我做了一些测试,看看这两种方法是如何比较的(直接从范围读取还是使用evaluate)。以下是我使用的代码:
Option Explicit
Private Declare Function GetTickCount Lib "kernel32.dll" () As Long
Sub test()
Dim ticks As Long
Range("A1:AA100000").Value = "adlrkjgalbabyajglakrjg"
ticks = GetTickCount
FINDIST1 ("baby")
Debug.Print "Read from range: ", GetTickCount - ticks
ticks = GetTickCount
FINDIST ("baby")
Debug.Print "Evaluate: ", GetTickCount - ticks
End Sub
Function FINDIST(stringToFind) As Long
FINDIST = Evaluate("SUM(IFERROR(SEARCH(" & Chr(34) _
& "*" & stringToFind & "*" & Chr(34) & "," _
& ActiveSheet.UsedRange.Address & ",1),0))")
End Function
Function FINDIST1(stringToFind) As Long
Dim counter As Long: counter = 0
Dim c As Range
Dim firstAddress As String
With ActiveSheet.UsedRange
Set c = .Find(stringToFind, LookIn:=xlValues, LookAt:=xlPart)
If Not c Is Nothing Then
firstAddress = c.Address
Do
counter = counter + 1
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
End With
FINDIST1 = counter
End Function
道格·格兰西提出了另一个非常好的观点,即可以使用COUNTIF
代替SEARCH
。这将导致一个非数组公式解决方案,并将主导我的原始公式,性能方面
这是道格的公式:
FINDIST_COUNTIF = ActiveSheet.Evaluate("COUNTIF(" _
& ActiveSheet.Cells.Address & "," & Chr(34) & "*" _
& stringToFind & "*" & Chr(34) & ")")
事实上,Doug的观点意味着没有必要使用Evaluate()
。我们可以从WorksheetFunction
对象调用Countif
。因此,如果目标是从电子表格调用此函数,则无需使用Evaluate()
或将其封装在UDF
中-这是一个典型的带有通配符的COUNTIF
应用程序
结果:
Read from range: 247,495 ms (~ 4 mins 7 secs)
Application.Evaluate: 3,261 ms (~ 3.2 secs)
Variant Array: 1,706 ms (~ 1.7 secs)
ActiveSheet.Evaluate: 1,257 ms (~ 1.3 secs)
ActiveSheet.Evaluate (DG): 602 ms (~ 0.6 secs)
WorksheetFunction.CountIf (DG):550 ms (~ 0.55 secs)
与使用Range.Find()
(?!)相比,Application.Evaluate
的速度大约快75倍。此外,原始代码(将Integer
更改为Long
)运行时间约为8秒
另外,在这种特殊情况下,Activesheet.Evaluate
实际上比Variant
数组更快。将CountIf
作为WorksheetFunction
方法调用与Evaluate
ing调用之间的区别似乎很小
警告:在UsedRange
中找到stringToFind
的频率可能会影响几种方法的相对性能。我运行了Activesheet.evaluation
和Variant Array
方法,使用上述范围(A1:AA100000)
,但只有前十个单元格具有匹配字符串
结果(平均6次,差异非常小):
这很有趣-在这种情况下,ActiveSheet.Evaluate
的性能似乎比variant数组稍好一些(除非我在循环代码中做了一些可怕的事情,在这种情况下请让我知道)。另外,变量
方法的性能实际上是。。相对于字符串的频率不变
运行在
Excel2010
上的Win7
替换函数FINDIST(stringToFind)
下,只要函数FINDIST(stringToFind)长
。将变暗计数器替换为整数
,将变暗计数器替换为长
。在结束函数之前添加FINDDIST=counter
。这将是非常缓慢的UsedRange
可以轻松地显示整个工作表。在VBA帮助中查找Find
,其中显示了如何查找字符串的每个匹配项。别忘了包括LookAt:=xlPart
。好的,你会提出一个有趣的话题。我确实阅读了Find方法文档,但没有看到它返回布尔值。我需要一个用于IF的布尔值。为什么需要布尔值?我假设它是您希望函数返回的匹配计数。只需将counter=counter+1
包含在Find
循环中,并返回counter
@TonyDallimore的最终值,谢谢您为我指明了正确的方向。看看答案和他们的评论,看看我们是如何解决的。
FINDIST_COUNTIF = ActiveSheet.Evaluate("COUNTIF(" _
& ActiveSheet.Cells.Address & "," & Chr(34) & "*" _
& stringToFind & "*" & Chr(34) & ")")
Read from range: 247,495 ms (~ 4 mins 7 secs)
Application.Evaluate: 3,261 ms (~ 3.2 secs)
Variant Array: 1,706 ms (~ 1.7 secs)
ActiveSheet.Evaluate: 1,257 ms (~ 1.3 secs)
ActiveSheet.Evaluate (DG): 602 ms (~ 0.6 secs)
WorksheetFunction.CountIf (DG):550 ms (~ 0.55 secs)
Activesheet.Evaluate: 920 ms (~ 1. sec)
Variant Array: 1654 ms (~ 1.7 secs)