比较一天,为什么31> = 20在这里返回False?

Roh*_*han 3 excel vba excel-vba

我正在调试这段代码,但我不确定为什么这会返回false而不是true.

?Day(i)>salday(0)
False
?Day(i)
 31 
?salday(0)
20
?isnumeric(day(i))
True
?isnumeric(salday(0))
True
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

Option Explicit
Option Compare Text

Sub genOP()

Dim wO As Worksheet
Dim i As Long, j As Long
Dim stDate, enDate, intVal, entR As Long, salDay, salAmt, stTime, enTime, dbMin, dbMax
Dim stRow As Long
Dim cet, curMn


'On Error Resume Next
Application.ScreenUpdating = False

stDate = STG.Range("B2"): enDate = STG.Range("B4")
intVal = Split(STG.Range("B3"), ","): entR = STG.Range("B5")
salDay = Split(STG.Range("B6"), "-")
salAmt = STG.Range("B7"): stTime = STG.Range("B8"): enTime = STG.Range("B9"): dbMin = STG.Range("B10"): dbMax = STG.Range("B11")

Set wO = ThisWorkbook.Sheets.Add
TEMP.Cells.Copy wO.Range("A1")

stRow = 19
curMn = Month(stDate)

For i = CLng(stDate) To CLng(enDate)

    If stRow > 19 Then
        wO.Rows(stRow & ":" & stRow).Copy
        wO.Rows(stRow + 1 & ":" & stRow + 1).Insert Shift:=xlDown
        Application.CutCopyMode = False
    End If

    cet = Trim(DESC.Range("A" & WorksheetFunction.RandBetween(2, DESC.UsedRange.Rows.Count)))

    If STG.Range("B14") = "ON" Then
       cet = cet & "Transaction amount " & Chr(34) & "&TEXT(H" & stRow & "," & Chr(34) & "#,##0.00" & Chr(34) & ")&" & Chr(34) & " GEL,"
    End If
    If STG.Range("B13") = "ON" Then
       cet = cet & Chr(34) & "&TEXT(B" & stRow & "-1," & Chr(34) & "dd mmm yyyy" & Chr(34) & ")&" & Chr(34)
    End If
    If STG.Range("B12") = "ON" Then
       cet = cet & " " & Format(stTime + Rnd * (enTime - stTime), "HH:MM AM/PM")
    End If

    If curMn = Month(i) And (Day(i) >= salDay(0) And Day(i) <= salDay(1)) Then  'Salary Day
        cet = Trim(DESC.Range("A" & WorksheetFunction.RandBetween(2, DESC.UsedRange.Rows.Count)))
        wO.Range("B" & stRow) = Format(i, "DD-MM-YYYY")
        wO.Range("I" & stRow) = salAmt
        wO.Range("L" & stRow) = MonthName(Month(i)) & "- Salome Baazov - " & "Geo" & " Ltd "
        curMn = WorksheetFunction.EDate(i, 1)
    Else
        wO.Range("B" & stRow) = Format(i, "DD-MM-YYYY")
        wO.Range("H" & stRow) = WorksheetFunction.RandBetween(dbMin, dbMax) + (WorksheetFunction.RandBetween(0, 1) * 0.5)
        wO.Range("L" & stRow) = "=" & Chr(34) & cet & Chr(34)
    End If

    stRow = stRow + 1
    i = i + intVal(WorksheetFunction.RandBetween(LBound(intVal), UBound(intVal))) - 1
Next i

wO.Rows(stRow).EntireRow.Delete
wO.Range("I" & stRow).Formula = "=SUM(I19:I" & stRow - 1 & ")"
wO.Range("H" & stRow).Formula = "=SUM(H19:H" & stRow - 1 & ")"

wO.Activate
Application.ScreenUpdating = True
STG.Range("B5") = stRow - 1
MsgBox "Process Completed"

End Sub
Run Code Online (Sandbox Code Playgroud)

A.S*_*S.H 6

因为你正在比较Variant不同类型的两个(事后我们讨论了...... thx @MatsMug).比较Variants不同类型,一个数字和一个String 时,比较结果是未定义的行为.

再一次是Variant异常.考虑一下这个MCVE:

Sub Test1()
  Dim i, salday
  i = CDate("5/30/2017")
  salday = Split("20-20-20", "-")

  Debug.Print Day(i), salday(0)                  '  30    20
  Debug.Print Day(i) > salday(0)                 '  False
  Debug.Print Day(i) > CStr(salday(0))           '  True
  '                    ^^^^
  Debug.Print Val(Day(i)) > salday(0)            '  True
  '           ^^^^
End Sub
Run Code Online (Sandbox Code Playgroud)

虽然salday(0)是String Variant,明确地将其转换为StringCStr解决的问题.但是,如果没有转换,则比较失败.VBA没有隐式地将数字转换为字符串,反之亦然.它比较了两种不同类型的变体并返回了垃圾结果.

有关Variant诅咒的更多信息,请阅读For v = 1 to v和For v in v - 不同类型的行为

事实证明,使用CLngVal强制数字比较是安全的方法,或CStr强制文本比较.


进一步考虑这三个简单的例子:

Sub Test1()
  Dim x, y: x = 30: y = "20"
  Debug.Print x > y               ' False !!
End Sub

Sub Test2()
  Dim x As Long, y: x = 30: y = "20"
  '       ^^^^^^
  Debug.Print x > y             ' True
End Sub

Sub Test3()
  Dim x, y As String:  x = 30: y = "20"
  '           ^^^^^^
  Debug.Print x > y             ' True
End Sub
Run Code Online (Sandbox Code Playgroud)

如您所见,当两个变量(数字和字符串)都被声明为变体时,比较就是垃圾.当其中至少有一个是显式的时,比较成功!

  • @ Mat'sMug这很棒.我几乎可以肯定RD正在成为VBA专业编程的必备条件. (4认同)

Mat*_*don 5

Dim stDate, enDate
Run Code Online (Sandbox Code Playgroud)

该指令声明了两个Variant变量.他们被分配到这里:

stDate = STG.Range("B2"): enDate = STG.Range("B4")
Run Code Online (Sandbox Code Playgroud)

假设[B2][B4]包含实际日期值,此时变量包含a Variant/Date.那是因为这里隐含的代码如下:

stDate = STG.Range("B2").Value: enDate = STG.Range("B4").Value
Run Code Online (Sandbox Code Playgroud)

但你可能已经知道了.继续.

salDay = Split(STG.Range("B6"), "-")
Run Code Online (Sandbox Code Playgroud)

salDay也是隐含的Variant.但是这条指令非常有用.这是隐式代码:

salDay = Split(CStr(STG.Range("B6").Value), "-")
Run Code Online (Sandbox Code Playgroud)

这会产生salDay一个字符串数组.所以我们在这里:

?Day(i)
 31 
?salday(0)
20
Run Code Online (Sandbox Code Playgroud)

前面的前方空间31是因为直接窗格总是留下负号的位置.salDay(0)作为一个String,没有领先的空间.那是你的线索.

?Day(i)>salday(0)
False
Run Code Online (Sandbox Code Playgroud)

随着salday(0)是一个String,我们在这里做一个字符串比较,如已经指出.除了31前面没有领先的空间; 隐式代码是这样的,因为类型Day(i)Integer:

?CStr(Day(i)) > salDay(0)
False
Run Code Online (Sandbox Code Playgroud)

解决方案是salDay完全摆脱:你不需要它.假设[B6]还包含一个实际日期,您可以Integer马上进入当天:

?Day(STG.Range("B6").Value)
Run Code Online (Sandbox Code Playgroud)

作为奖励,您可以将代码与工作表中基础日期值的字符串表示形式分离,因此更改NumberFormat不会破坏您的代码.始终对待日期!