vba - 循环遍历行计数并将值保存到变量

时间:2017-11-12 13:06:20

标签: excel vba excel-vba

几乎相同的代码怎么能导致不同的输出呢?

此代码:

Sub test()
  i = 1
    For d = 1 To 4
      i = i + 1
      Debug.Print i
    Next d
End Sub

打印
 2
 3
 4
 5

这是完美的,但这一个

Sub test2()
  rcount = 34
    For d = 1 To 4
     rcount = Sheets("sheettest").Range(Range("A" & rcount + 4), Range("B" & rcount + 4).End(xlDown)).Offset(3, 0).Rows.Count
    Debug.Print rcount
    Next d
End sub

打印
 203个
 34
 203
 34

这不是我的预期

为什么第二个值又是34?

1 个答案:

答案 0 :(得分:0)

我知道你是如何实现这一目标的,并且无法立即解决问题,但我相信这将以稍微不同的方式完成同样的事情。

Sub ListSectionCounts()

    Const colToCheck = 1 'check column A
    Dim sh As Worksheet, sectionRows As Long, sectionCount As Long, rw As Range

    Set sh = ActiveSheet 'use the currently selected sheet

    For Each rw In sh.UsedRange.Rows 'loop through all rows that have data
        If sh.Cells(rw.Row, colToCheck) <> "" Then
            'this row has data so count it
            sectionRows = sectionRows + 1
        Else
            'this row does not have data
            If sectionRows > 0 Then
                'if this is the 1st row without data then print the count and reset the counter
                sectionCount = sectionCount + 1
                Debug.Print "section #" & sectionCount & " row count: " & sectionRows
                sectionRows = 0
            End If
        End If
    Next rw

    'check if there's one more section to list
    If sectionRows > 0 Then
       sectionCount = sectionCount + 1
       Debug.Print "section #" & sectionCount & " row count: " & sectionRows
    End If

    Debug.Print "Finished (" & sectionCount & " sections)"
End Sub

(按原样尝试,但是您可能需要更改活动表中的工作表,如果需要它来检查除#34; A&#34;之后的列,然后更改colToCheck。)

编辑:继续发表评论,这是相同的代码,相同的结果,但是压缩并且更难以弄清楚发生了什么:

Sub secCnt()
For Each rw In ActiveSheet.UsedRange.Rows
  If Cells(rw.Row, 1) <> "" Then
    sRw = sRw + 1
  ElseIf sRw > 0 Then Debug.Print "rows:" & sRw: sRw = 0
  End If
Next rw
If sRw > 0 Then Debug.Print "rows:" & sRw
End Sub

&#34;我如何解决问题...&#34;

我知道你使用了我的&#34;备用解决方案&#34;但你仍然想知道你的代码首先出现了什么问题,就像我一样。因为我没有写它,我不确定你到底是怎么实现目标的,直到我分裂了将问题转化为很多较小的碎片,逐步穿过它,并将它重新放回原处。在进行故障排除时,我倾向于保留一个临时的Notepad ++文件,所以我想我会分享,还有其他人问我有关我的故障排除过程。

请记住,我是自学成才的(谷歌,宝贝!)所以我可能会打破一些编码标准。编码中的标准化存在是有充分理由的,而且我总是在学习,但最终我想到了“无论如何完成工作”#34; 最终是正确的方式。我决不是说'#34;我的方式是正确的方式&#34;因为有很多&#34;对&#34;解决问题的方法。

(现在我用我的部分格式化的笔记粘贴,然后我会回来重新格式化!)

我并不意味着劫持你的帖子,但这是一个很好的例子,我想与可能感兴趣的其他人分享!

'Option Explicit

'How I trouble-shot the issue (explained the long way for future illustration purposes)
'Note I replaced `Sheets("sheettest")` with `ActiveSheet` for testing purposes; you may need to change that back

'I need to return the values:     11, 9, 7, 4    (based on my sample dataset)
'The problem code below returns:  11, 20, 11, 20  ...but why?
Sub test2_orig()
  rcount = 34
    For d = 1 To 4
     rcount = ActiveSheet.Range(Range("A" & rcount + 4), Range("B" & rcount + 4).End(xlDown)).Offset(3, 0).Rows.Count
    Debug.Print rcount
    Next d
End Sub

首先,我想确切地看到每一步的发生情况。这可以通过&#34; Watching&#34;变量(VBE调试菜单),但我倾向于在Debug.Print上沉重。

Sub test2_1()
  rcount = 34
    For d = 1 To 4
     Debug.Print "d=" & d
     Debug.Print "rcount (before calculation)=", rcount
     rcount = ActiveSheet.Range(Range("A" & rcount + 4), Range("B" & rcount + 4).End(xlDown)).Offset(3, 0).Rows.Count
     Debug.Print "the calculation:", , "debug.print ActiveSheet.Range(Range(""A" & rcount + 4 & """), Range(""B" & rcount + 4 & """).End(xlDown)).Offset(3, 0).Rows.Count"
     Debug.Print "rcount (after calculation)= ", rcount
     Debug.Print "range (used in calculation)=", ActiveSheet.Range(Range("A" & rcount + 4), Range("B" & rcount + 4).End(xlDown)).Offset(3, 0).Address
     Debug.Print
   Next d
End Sub

OUTPUT (Sub test2_1):

d=1
rcount (before calculation)= 34
the calculation:             debug.print ActiveSheet.Range(Range("A15"), Range("B15").End(xlDown)).Offset(3, 0).Rows.Count
rcount (after calculation)=  11
range (used in calculation)= $A$18:$B$37

d=2
rcount (before calculation)= 11
the calculation:             debug.print ActiveSheet.Range(Range("A24"), Range("B24").End(xlDown)).Offset(3, 0).Rows.Count
rcount (after calculation)=  20
range (used in calculation)= $A$27:$B$37

d=3
rcount (before calculation)= 20
the calculation:             debug.print ActiveSheet.Range(Range("A15"), Range("B15").End(xlDown)).Offset(3, 0).Rows.Count
rcount (after calculation)=  11
range (used in calculation)= $A$18:$B$37

d=4
rcount (before calculation)= 11
the calculation:             debug.print ActiveSheet.Range(Range("A24"), Range("B24").End(xlDown)).Offset(3, 0).Rows.Count
rcount (after calculation)=  20
range (used in calculation)= $A$27:$B$37

对于您的预期算法仍然有点不清楚,最好将该过程分解为可能的最小步骤,以确定问题所在。不要Loop然而,只是试着让第一个数字正确,并通过大量评论来谈谈自己

Sub test2_2()
'get to first section (store row number in it's own variable)
Dim rowNum As Integer
rowNum = 34
'move down 4 rows to skip header
rowNum = rowNum + 4  ' = 38
'start range at A38 (and store it in a range object)
Dim rgStart As Range
Set rgStart = Range("A" & rowNum)
'end range at B.."End(xlDown)" (store it in a range object)
Dim rgEnd As Range
Set rgEnd = Range("B" & rowNum).End(xlDown)
'combine them into a single range object
Dim rg As Range
Set rg = Range(rgStart, rgEnd)
'count rows in the range (store separately)
Dim sectionCount As Integer
sectionCount = rg.Rows.Count
Debug.Print rgStart.Address & " to " & rgEnd.Address & " Row.Count = " & sectionCount
'this returned 11 (correct with my dataset)

'now we want to skip three rows and get the next range
'  (at this point I realized 1 of 2 issues with your original code, but I'll get back to that!)

'Rownum is still 38. RowNum + sectionCount + 3 will be the start of the headers of the next section:
rowNum = rowNum + sectionCount + 3
Debug.Print "next rowNum=" & rowNum

'theoretically we should be able to loop now, starting with the "skip 4 header rows" line...
'...instead of changing this sub, we can make a 2nd copy, saving this one for reference ("last working version") as we proceed
'...while tidying, we relocate all our variable declarations to the top of the new sub , and ensure Heavy Commenting for the sake of self & others
End Sub

'Our "tidied" version: (only cosmetic changes for now)
Sub test2_3()
Dim rowNum As Integer 'the current row number
Dim rgStart As Range 'start of the range for the current section
Dim rgEnd As Range 'end of the range for the current section
Dim rg As Range 'the entire range for the current section
Dim sectionRows As Integer 'count of the rows for the current section

rowNum = 34 'get to first section (begin loop AFTER here)

'BEGIN LOOP:
rowNum = rowNum + 4  'skip header (4 rows)
Set rgStart = Range("A" & rowNum) 'start range at A38
Set rgEnd = Range("B" & rowNum).End(xlDown) 'end range at column B, bottom row of this section
Set rg = Range(rgStart, rgEnd) 'combine into single range object
sectionRows = rg.Rows.Count 'count rows in the range

Debug.Print rgStart.Address & " to " & rgEnd.Address & " Row.Count = " & sectionRows 'for now stick with just debug.print'ing RowCount
'(a manual count tells me I'm expecting results: 11 -> 9 -> 7 -> 4 -> 2 using my sample dataset)
rowNum = rowNum + sectionRows + 3 'skip to top of next section
Debug.Print "next rowNum=" & rowNum

'************* Does it still work? Run it again; execution will `stop` ('break') on this line:
Stop '******** Drag the yellow marker up to the line after "BEGIN LOOP" and F5 to continue execution from there.

End Sub
好吧,数字是正确的!现在自动化循环。 For..Next计数器不适合这种情况,因为数据往往会发生变化。相反,在循环的每次迭代结束时,我们将检查是否有4行以下的数据&#39; rgEnd&#39;。 (如果是的话,再次循环播放。)

Sub test2_4() 'Semi-Final Version:
    Dim rowNum As Integer 'the current row number
    Dim rgStart As Range 'start of the range for the current section
    Dim rgEnd As Range 'end of the range for the current section
    Dim rg As Range 'the entire range for the current section
    Dim sectionRows As Integer 'count of the rows for the current section

    rowNum = 34 'get to first section (begin loop AFTER here)

    Do 'BEGIN LOOP:
        rowNum = rowNum + 4  'skip header (4 rows)
        Set rgStart = Range("A" & rowNum) 'start range at A38
        Set rgEnd = Range("B" & rowNum).End(xlDown) 'end range at column B, bottom row of this section
        Set rg = Range(rgStart, rgEnd) 'combine into single range object
        sectionRows = rg.Rows.Count 'count rows in the range

        Debug.Print rgStart.Address & " to " & rgEnd.Address & " Row.Count = " & sectionRows 'for now stick with just debug.print'ing RowCount
        '(a manual count tells me I'm expecting results: 11 -> 9 -> 7 -> 4 -> 2 using my sample dataset)
        rowNum = rowNum + sectionRows + 3 'skip to top of next section
    Loop Until rgEnd.Offset(4, 0) = "" 'loop until there ISN'T data 4 rows below rgEnd
    Debug.Print "Done!"
End Sub

现在我们的子功能就像我们想要的那样。没有理由不能保持这样,但许多开发者(包括我自己)可能会特别关注缩小代码。像这样反向更平滑;只记得尽可能保持评论,不要过度使用!

'Final Version:
Sub test2_5_Final()
    Dim rowNum As Integer, sectionRows As Integer, sectionNum As Integer 'counters: current row, rows in section, section number
    Dim rgStart As Range, rgEnd As Range, rg As Range 'start/end/complete ranges for "current section"

    rowNum = 34 'first section's top row
    Do
        rowNum = rowNum + 4  'skip header
        Set rgStart = Range("A" & rowNum) 'section top-left
        Set rgEnd = Range("B" & rowNum).End(xlDown) 'section bottom-right
        Set rg = Range(rgStart, rgEnd) 'whole section
        sectionRows = rg.Rows.Count 'count rows in section
        sectionNum = sectionNum + 1 'increase count of sections
        Debug.Print "Section #" & sectionNum & " = " & sectionRows & " rows" 'store the values somewhere else if need be
        rowNum = rowNum + sectionRows + 3 'skip to top of next section
    Loop Until rgEnd.Offset(4, 0) = "" 'loop until there's no data 4 rows down
    Debug.Print "Done!"
End Sub

最后,通过故障排除&amp;重写我意识到原始代码的问题:

  1. 变量rcount用于两个当前行#和当前部分中的行数,从而交替每次运行

  2. Offset错误网站上的右括号指的是整个范围,而不仅仅是它的底部。

  3. 解决方案:更正了括号并添加了变量。上面的代码可能会更可靠,但仅仅是为了它:

    Sub test2_orig_fixed()
        Dim d As Integer, rcount As Integer, secCount As Integer
        rcount = 34
        For d = 1 To 4
            secCount = ActiveSheet.Range(Range("A" & rcount + 4), Range("B" & rcount + 4).End(xlDown).Offset(3, 0)).Rows.Count
            Debug.Print secCount - 3
            rcount = rcount + secCount + 4
        Next d
    End Sub
    

    养成有条不紊地解决这个例子的小问题的习惯,在将来处理更大的问题时,将会有所帮助。

    每个人的大脑都以不同的方式解决逻辑问题。这就是 I 处理它的方式,但是有无数其他方法(甚至还有关于故障排除理论的大学级课程。)当我们有代码时,谁需要谜题!