Application.Match不是确切的值

时间:2018-02-20 12:41:34

标签: excel vba excel-vba

有一段代码可以查找2张纸之间的匹配(sheet1是客户列表,rData是复制pdf和发票)。它通常是完全匹配的,但在某些情况下,我正在寻找匹配rData的6个第一个字符

Dim rData   As Variant
Dim r       As Variant
Dim r20  As Variant
Dim result  As Variant
Dim i       As Long

rData = ActiveWorkbook.Sheets(2).Range("A1:A60000") 

r20 = ActiveWorkbook.Sheets(1).Range("C2:C33")

For Each r In r20
  result = Application.Match(r, rData, 0)
  If Not IsError(result) Then
    For i = 1 To 5
      If (result - i) > 0 Then
        If (Left(Trim(rData(result - i, 1)), 3) = "418") Then
          MsgBox "customer: " & r & ". invoice: " & rData(result - i, 1)
        End If
      End If
    Next
    For i = 1 To 15
      If (result + i) > 0 Then
        If (Left(Trim(rData(result + i, 1)), 3) = "418") Then
          MsgBox "customer: " & r & ". invoice: " & rData(result + i, 1)
        End If
      End If
    Next
  End If
Next r

End Sub

只有部分令我头疼的是这部分result = Application.Match(r, rData, 0)。如何匹配不完全匹配?

Sheet1的样本

这或多或少看起来像。在CustomerNumber#之后进行匹配很容易,因为每张发票都是相同的。但有时发票没有它,所以我在CustomerName之后搜索,有时它们有大写字母,有时它背后有额外的东西,因此无法找到完全匹配。 希望它有意义。

1 个答案:

答案 0 :(得分:1)

要将客户列表中的客户名称与发票中的客户名称相匹配,即使附加了额外字符,也可以使用*中的通配符Match()

Match()函数中也有拼写错误。 r20应为rData

这是您应用修补程序的代码:

Sub Test()
  'v4
  Dim rData   As Variant
  Dim r       As Variant
  Dim r20  As Variant
  Dim result  As Variant
  Dim i       As Long

  rData = ActiveWorkbook.Sheets(2).Range("A1:A60000")

  r20 = ActiveWorkbook.Sheets(1).Range("C2:C33")

  For Each r In r20
    result = Application.Match(r & "*", rData, 0) ' <~ Fixed here
    If Not IsError(result) Then
      For i = 1 To 5
        If (result - i) > 0 Then
          If (Left(Trim(rData(result - i, 1)), 3) = "418") Then
            MsgBox "customer: " & r & ". invoice: " & rData(result - i, 1)
          End If
        End If
      Next
      For i = 1 To 15
        If (result + i) > 0 Then
          If (Left(Trim(rData(result + i, 1)), 3) = "418") Then
            MsgBox "customer: " & r & ". invoice: " & rData(result + i, 1)
          End If
        End If
      Next
    End If
  Next r

End Sub

备注:

  • Match()不区分大小写,因此可以使用不同的大写字母。
  • Sheets(2)中的数据必须都是Match()的文字才能正常使用通配符。

EDIT1: 新的更好的版本

EDIT2: 重构常数并使数据范围动态

EDIT3: 允许任何前缀到固定长度的发票号

以下是您的代码的更好的重写版本:

Sub MuchBetter()
  'v3
  Const s_InvoiceDataWorksheet As String = "Sheet2"
  Const s_InvoiceDataColumn    As String = "A:A"
  Const s_CustomerWorksheet    As String = "Sheet1"
  Const s_CustomerStartCell    As String = "C2"
  Const s_InvoiceNumPrefix     As String = "418"
  Const n_InvoiceNumLength       As Long = 8
  Const n_InvScanStartOffset     As Long = -5
  Const n_InvScanEndOffset       As Long = 15

  Dim ƒ As Excel.WorksheetFunction: Set ƒ = Excel.WorksheetFunction ' Shortcut

  With Worksheets(s_InvoiceDataWorksheet).Range(s_InvoiceDataColumn)
    With .Parent.Range(.Cells(1), .Cells(Cells.Rows.Count).End(xlUp))
      Dim varInvoiceDataArray As Variant
      varInvoiceDataArray = ƒ.Transpose(.Cells.Value2)
    End With
  End With
  With Worksheets(s_CustomerWorksheet).Range(s_CustomerStartCell)
    With .Parent.Range(.Cells(1), .EntireColumn.Cells(Cells.Rows.Count).End(xlUp))
      Dim varCustomerArray  As Variant
      varCustomerArray = ƒ.Transpose(.Cells.Value2)
    End With
  End With

  Dim varCustomer As Variant
  For Each varCustomer In varCustomerArray
    Dim dblCustomerIndex As Double
    dblCustomerIndex = Application.Match(varCustomer & "*", varInvoiceDataArray, 0)
    If Not IsError(dblCustomerIndex) _
    And varCustomer <> vbNullString _
    Then
      Dim i As Long
      For i = ƒ.Max(dblCustomerIndex + n_InvScanStartOffset, 1) _
          To ƒ.Min(dblCustomerIndex + n_InvScanEndOffset, UBound(varInvoiceDataArray))
        Dim strInvoiceNum As String
        strInvoiceNum = Right$(Trim$(varInvoiceDataArray(i)), n_InvoiceNumLength)
        If (Left$(strInvoiceNum, Len(s_InvoiceNumPrefix)) = s_InvoiceNumPrefix) Then
          MsgBox "customer: " & varCustomer & ". invoice: " & strInvoiceNum
        End If
      Next
    End If
  Next varCustomer

End Sub

备注:

  • 使用常量是个好主意,因此所有文字值只输入一次并保持组合在一起。
  • 使用RVBA命名约定极大地提高了代码的可读性,并降低了错误的可能性。
  • 使用长的,适当命名的变量使代码基本上自我记录。
  • 强烈建议在每次读取单元格值时使用.Value2(它可以避免隐式转换,使其速度稍快,并且可以消除由转换引起的某些问题)。
  • 令人惊讶的是,在VBA中,有充分的理由将变量声明尽可能接近第一次使用变量。两个这样的原因是1)它提高了可读性,2)它简化了未来的重构。请记住,每次遇到Dim时都不会重新初始化变量。初始化仅在第一次发生。
  • 根据DRY原则将双环组合成一个。
  • 虽然如果你可以保证永远不会这样检查一个空的客户名称/号码,那么这是一个很好的防御性编程,因为空值会导致错误的结果。
  • 循环内的否定索引检查已被删除,并替换为Max()语句中For工作表函数的一次性使用。
  • Min()工作表函数也用在For语句中,以避免尝试读取数组末尾。
  • 除非您明确检查错误,否则始终在WorksheetFunction对象上使用工作表函数,在这种情况下使用Application对象。