【问题标题】:Excel VBA: `range.find()` does not find values which are displayed as `####`Excel VBA:`range.find()` 找不到显示为 `####` 的值
【发布时间】:2019-09-19 22:31:52
【问题描述】:

我正在尝试在特定列中查找特定值。例如B 列中的值100000。以下代码仅在列宽到足以显示完整数字时才有效:

Dim rngSearchRange As Range
Set rngSearchRange = ThisWorkbook.Worksheets(1).Columns(2)
Dim searchTerm As Variant
searchTerm = 100000

Dim rngResultRange As Range       
Set rngResultRange = rngSearchRange.Find(What:=searchTerm, lookin:=xlValues, lookat:=xlWhole)

一旦列变窄,因此 Excel 仅在特定单元格中显示 ##### 而不是 100000,查找方法返回 Nothing

有没有办法根据实际值而不是值的显示来使用查找方法?如果没有,是否有任何替代 For Each cell In rng.Cells 的方法?最终,我正在寻找使用最少资源的方法。

注意:searchRange 只有一列,searchValue 要么不存在,要么只存在一次。

注意:使用 match() 时有一个 followup question 注意:尽管数据和代码都没有改变,但它似乎有时会起作用。不幸的是,我无法重现更改。这整个事情可能确实是一个错误

【问题讨论】:

  • 尝试将lookat:=xlWhole更改为lookin:=xlValues
  • 可以重现 Find 失败。自动调整列是一种选择吗?
  • 或者正在使用Application.Match 选项?
  • @ScottCraner 我无法摆脱lookat:=xlWhole(因为它不会给我正确的结果),但我尝试添加lookin:=xlValues - 没有变化。
  • 经过测试,我发现LookIn:=xlFormulas 在所有情况下都有效。如果您不指定此参数,它将继承上次使用时的设置。

标签: excel vba


【解决方案1】:

如果列宽太窄,可以重现 Find 失败。

Match 没有这个问题。

Sub dural()
    Dim rngSearchRange As Range
    Set rngSearchRange = ThisWorkbook.Worksheets(1).Columns(2)
    Dim searchTerm As Variant
    searchTerm = 100000
    Dim rngResultRange As Range

    Dim found As Variant
    found = Application.Match(searchTerm, rngSearchRange, 0)

    If Not IsError(found) Then
        Set rngResultRange = rngSearchRange.Cells(found)
        MsgBox rngResultRange.Address
    End If
End Sub

根据您的用例,这可能是一个选项,或者如果不是,也许Range.AutoFit?虽然“我正在尝试在特定列中查找特定值”,但听起来这可能是一个选项。

【讨论】:

  • 谢谢,我试试看。我想我也可以在使用 range.find() 之前尝试使用 VBA 来拟合列(然后将其放回原来的宽度),但这似乎不是一个好的解决方案,因为它取决于值,这是我无法控制的。至少我必须确保在安装列后,所有值都是完全可见的。有没有办法做到这一点?
  • Range.AutoFit 就是这样做的。
  • 我知道,但如果我这样做解决方法,我宁愿单独验证正确的结果。不确定这是否可以做到。或者 AutoFit 是否至少在执行后返回任何结果(特别是如果它失败但没有产生错误)?
  • 我不确定你的意思。我不确定 AutoFit 会如何失败。
  • (无论如何+1)我不能给你一个具体的场景,因为这本身就是验证的原因。但是让我们说,它由于某种未知的原因没有执行。可能的验证是检查搜索范围内是否没有##### 值。基本上,就像design by contract 一样,我只是在检查后置条件。
【解决方案2】:

您可以将范围放入一个数组并循环该数组,或者只使用 MATCH:

    Sub test()
        Dim rngSearchRange, rngResultRange As Range
        Dim searchTerm As Variant
        Dim vRow As Variant

        Set rngSearchRange = ThisWorkbook.Worksheets(1).Columns(2)
        searchTerm = 10000
        vRow = Application.Match(searchTerm, rngSearchRange, 0)
        If Not IsError(vRow) Then
            Set rngResultRange = rngSearchRange.Resize(1, 1).Offset(vRow - 1, 0)
        Else
            MsgBox "Not Found"
        End If
End Sub

【讨论】:

  • 谢谢,但正如我在问题中提到的那样,循环需要太多时间。现在我看不出与 BigBen 的答案有什么不同(使用匹配),还是我遗漏了什么?
  • @Albin - 循环数组比 For Each cel In rng.Cells 快很多。
  • @BigBen 是的,但如果我没记错的话,填充数组需要循环遍历每个单元格,所以最后我必须循环两次(通过单元格和数组) !?还是有更快的方法?查尔斯的比赛例子和你的基本一样,对吧?
  • 不,您可以用一行将范围读入数组。请参阅here 以获得很好的解释。遍历数组非常快。击中每一个细胞是很慢的。
  • @Albin 是否可以只循环 rngSearchRange .Columns(或 rngSearchRange .Rows - 以较小者为准!),然后在每个循环上执行 Match 直到找到结果?
【解决方案3】:

试试这个:

Sub test()
   Dim rngSearchRange, rngResultRange As Range
   Dim searchTerm As Variant

   Set rngSearchRange = ThisWorkbook.Worksheets(1).Columns(2)
   searchTerm = 10000
   Set rngResultRange = rngSearchRange.Find(what:=searchTerm, LookIn:=xlValues)
End Sub

【讨论】:

  • 我看不出我的代码有什么不同(除了您将 rngSearchRange 定义为 Variant)?!并且将其定义为 Variant 也无济于事,为什么要有所帮助?
  • 与您的原始问题相比,我对两个范围都使用了“set”,并更改了 find 的参数。我在一些数据上对其进行了测试,它运行良好,所以我希望它也适用于您的数据。此外,如果您阅读代码,rngSearchRange 被声明为 Range,而不是 Variant。
  • 谢谢!!丢失的集合只是我的问题中缺少,而不是我的原始代码中,所以这不是问题(我更正了我的问题)。我忽略了参数,但实际上,我不能使用你的参数,因为它不包含lookat=xlWhole。我需要它来搜索(请参阅我的问题)。不会将 rngSearchRange 声明为 Variant 请参阅 here,或者他们是否更改了 VBA 中的语法?
  • @CADV Albin 是对的,你必须显式声明变量类型,否则是Variant:Dim rngSearchRange as Range, rngResultRange as Range
【解决方案4】:

find 的问题在于它仅出于某种原因查找显示的值,这与您按 crtl+F 或单击“主页”功能区上的“查找并选择”选项时的搜索框行为相同。目前没有已知的方法来解决这个问题(查看 xlValues 等 cmets 指出的) 由于有多种方法可以解决这个问题,(最慢)但最可靠的方法是使用 foreach 循环:

For Each cel In rngSearchRange
If cel.Value = searchTerm Then
Set rngResultRange = cel
exit for '<-If you want the first result, leave this. If you want the last result, omit. Using the first result could be significantly quicker as it will stop looping right away.
End If
Next cel

只需确保将范围设置为确定值,例如 Range("A1:B87") 而不是 Columns(2),因为这会引发类型不匹配错误。如果要搜索 B 列,请改用Range("B:B")

【讨论】:

  • 我认为,将范围读入数组并在数组上使用循环(正如已经在不同的答案中建议的那样)应该更快。不过,我仍然需要对其进行测试。
  • 这确实是我的想法之一,而且确实可能​​会稍微快一些。但是我没有一个大的数据集来测试它,所以我选择了一些我确定的东西。如果你都尝试了,你能告诉我哪个更快吗?
  • 肯定的,我会的
【解决方案5】:

这是一个作弊版本:它将范围复制到临时工作表,将公式转换为值,并在那里进行查找。

Public Function FindValueInRange(ByVal RangeToSearch As Range, ByVal ValueToFind As Variant) As Range
    Dim WasActive As Worksheet, ScreenUpdating As Boolean, Calculation As XlCalculation
    'Store current position
    Set WasActive = ActiveSheet
    ScreenUpdating = Application.ScreenUpdating
    Application.ScreenUpdating = False
    Calculation = Application.Calculation
    Application.Calculation = xlCalculationManual

    'Let's get to work!
    Set FindValueInRange = Nothing 'Default to Nothing
    On Error GoTo FunctionError

    Dim TempSheet As Worksheet, FoundCell As Range, DisplayAlerts As Boolean

    'Create Temp Sheet
    Set TempSheet = Worksheets.Add

    'Copy data to Temp Sheet, in the same location
    TempSheet.Range(RangeToSearch.Address(True, True, xlA1, False)).Value = RangeToSearch.Value

    'Column Width to Maximum!
    TempSheet.Range(RangeToSearch.Address(True, True, xlA1, False)).EntireColumn.ColumnWidth = 255

    'Search the cells in the Temp Sheet
    Set FoundCell = TempSheet.Range(RangeToSearch.Address(True, True, xlA1, False)).Find(ValueToFind, LookIn:=xlFormulas, LookAt:=xlWhole)

    'Return the found cell, but on the original Worksheet
    If Not (FoundCell Is Nothing) Then Set FindValueInRange = RangeToSearch.Worksheet.Range(FoundCell.Address(True, True, xlA1, False))

    'Remove the Temp Sheet
    DisplayAlerts = Application.DisplayAlerts
    Application.DisplayAlerts = False
    TempSheet.Delete
    Application.DisplayAlerts = DisplayAlerts
    Set TempSheet = Nothing

FunctionError:
    On Error GoTo -1 'Reset the error buffer
    'Restore previous position
    WasActive.Activate
    Application.Calculation = Calculation
    Application.ScreenUpdating = ScreenUpdating
End Function

然后会像这样使用:

Set rngResultRange = FindValueInRange(rngSearchRange, searchTerm)

【讨论】:

  • 谢谢,虽然这是摆脱公式的好方法,但并不能解决最初的问题。
  • @Albin 因为它摆脱了公式,所以它可以使用LookAt:=xlFormulas(无论显示宽度如何,它都应该工作)。或者,您可以设置 EntireColumn.ColumnWidth=255 以使列真的宽吗?
  • 我不认为 LookAt:=xlFormulas 解决了这个问题,但我会再试一次。但我认为我更喜欢匹配的解决方案,因为它更容易实现并且不需要太多的处理时间/内存(至少对于这种情况/问题)
猜你喜欢
  • 2015-11-10
  • 1970-01-01
  • 1970-01-01
  • 2016-06-05
  • 2021-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-10
相关资源
最近更新 更多