【问题标题】:Counting Non-Blank Cells Under a Given Heading计算给定标题下的非空白单元格
【发布时间】:2014-04-04 00:13:45
【问题描述】:

我有很多类似的报告,这些报告在第一列中都有一个“名称:”单元格,并且在同一列中列出了一组对象,例如“对象”。在字符串“Name:”的两个实例和中间的一些其他行(空白和填充)之后,我们到达“Objects”字符串。在“对象”一词的每个实例之后,都有一个数字列表,用于计算对象的总数。每个列表中有任意数量的对象,但它们总是位于标题“对象”之后的事实是不变的。是否有一种简单的方法可以让 Excel 搜索每个报告并将在某个单元格中的“名称:”下找到的名称以及在其旁边的“对象”一词之后的所有非空白单元格的计数?由于对象列表是完整的,第一个空白单元格可能会触发计数停止并移至下一个列表,而大量空白单元格(例如超过 300 个)可能会触发程序声明不再有条目拉出并停止)。

示例

    A              B           C

    Name:      John Smith


    Date:       March 5th

    Name:       John Smith

   Objects
    List 
      1            Filler1     Something1
      2            Filler2     Something2
      3            Filler3     Something3
      4            Filler4     Something4
      5            Filler5     Something5
      6            Filler6     Something6

    Name:      Jane Doe


    Date:       March 8th

    Name:       Jane Doe

   Objects
    List 
      1            Filler1     Something1
      2            Filler2     Something2
      3            Filler3     Something3
      4            Filler4     Something4

结果会是这样的:

    John Smith         6
    Jane Doe           4

【问题讨论】:

  • 能否提供屏幕截图和/或示例数据以及预期结果?这样更容易传达你想要的。
  • @Nanashi 我绝对可以添加一些示例数据。
  • 好的,我现在看看(数据)情况是什么样的。您还可以展示结果应该是什么样子吗?你的问题不是很清楚......
  • but the fact that they always come after the title "Objects" is constant. 我猜你的意思是Object 而不是Objects?是的,您可以通过使用.Find 找到包含该单词的单元格,然后简单地获取相关数字,轻松实现您想要的。 THIS 将帮助您入门。
  • 另外看看CountA函数,结合find的结果,我想会得到你想要得到的地方。

标签: vba excel


【解决方案1】:

修订 4/3:更改对象名称;更改案例; 4月2日再次修订:搜索特定类型的对象;修订 4/2;跳过第一个“名称”;如果达到最大值,则结束循环(输入格式问题!) 4/1 修订;添加了错误陷阱和行号以查找错误。将有助于查看用户输入。请参阅代码中的注释。

    Option Explicit
' Assumptions:
' (1) All data in first column - except for name.
' (2) The literal 'Name:' will be in Col A; The name (i.e. 'John Doe') will be in Col B.
' (3) The same 'Name' will appear twice, with a 'Date' row between the two.
' (4) May be blank row(s) anywhere before or after row containing 'Name'.
' (5) 'Object' row will have string starting with 'Objects' in Col A, followed by Object Name (i.e. 'Objects Cars')
' (6) 'Object' row may repeat for ONE name.
' (7) Search for user specified Object in list for a Name. Set to zero if not found
' (8) Name will be repeated many times in the column (>100,000 rows).

' For test purposes, I have used 'Sheet1' as report sheet, and 'Sheet2' as output.
' Can change to process ALL sheets in a workbook (not sure how your reports are found (.. sheets or workbooks..)

Sub Create_Summary()
Dim lLastRow    As Long
Dim lRow        As Long
Dim lOutRow     As Long
Dim lNameRow    As Long

Dim sName       As String
Dim iNameCtr    As Integer
Dim lRowCt      As Long
Dim blnSkip     As Boolean
Dim strObjName  As String
Dim strObjKey  As String
Dim strObjNameFound As String

1000  On Error GoTo Error_Trap

'Get last used row
1010  lLastRow = Cells.Find(What:="*", After:=Range("A1"), SearchOrder:=xlByRows, SearchDirection:=xlPrevious).row
1020  Debug.Print "Total Rows: " & lLastRow

1030  strObjKey = "Objects"     ' <<<<<<< Add code to obtain and set to whatever you want.
1040  strObjName = "Cars"     ' <<<<<<< Add code to obtain and set to whatever you want.

1050  lOutRow = 1
1060  blnSkip = False
1070  For lRow = 1 To lLastRow
1080      iNameCtr = 0
1090      lRowCt = 0
1100     Do Until iNameCtr = 2 Or lRow >= lLastRow    'Trim(Cells(lRow, 1)) = "Name:" Or lRow >= lLastRow      ' Find 'Name'
1110          If Trim(Cells(lRow, 1)) = "Name:" Then
1120              iNameCtr = iNameCtr + 1
1130              lNameRow = lRow
1140          End If
1150          lRow = lRow + 1
1160       Loop
1170      lRow = lRow - 1
1180      If lRow >= lLastRow - 1 Then Exit For
1190      If blnSkip = True Then
1200         sName = Cells(lRow, 2)            ' Name is in Col 2
1210         Debug.Print "Row: " & lRow & vbTab & ">> Name: " & sName
1220         Sheets("Sheet2").Range("A" & lOutRow) = sName      ' Save Name
            ' There will always be a non-blank row after 'Name' do not count that!
1230         lRow = lRow + 1
1240         Do Until LCase(Left(Cells(lRow, 1), 7)) = LCase(strObjKey) And InStr(8, LCase(Cells(lRow, 1)), LCase(strObjName)) > 0    ' Find 'Object'
1250             lRow = lRow + 1
1260             If LCase(Trim(Cells(lRow, 1))) = LCase("Name:") Then     ' Means never found desired 'Objects'
1270                 Sheets("Sheet2").Range("B" & lOutRow) = 0
1280                 lRow = lRow - 1
1290                 lOutRow = lOutRow + 1
1295                 GoTo Next_Row
1300             ElseIf lRow > lLastRow Then
1310                 Sheets("Sheet2").Range("B" & lOutRow) = lRowCt
1320                 Debug.Print "**** Exit because at end of used range!"
'134                 MsgBox "Found name: '" & sName & "' at row " & lNameRow & ", but there was no matching 'Objects'", vbOKOnly, "Sheet Format Incorrect"
1330                 Exit For
1340             End If
1350         Loop
1360         Debug.Print "Row: " & lRow & vbTab & ">> " & strObjKey & ": " & Cells(lRow, 1)
1370         strObjNameFound = Trim(Mid(Cells(lRow, 1), 8, 99))
1380         lRow = lRow + 2   ' Must skip a 'filler' line after 'Objects'

1390         Do Until Cells(lRow, 1) = "" Or LCase(Left(Cells(lRow, 1), 7)) = LCase(strObjKey) Or lRow >= lLastRow      ' Find Blank line
1400             If Cells(lRow, 1) <> "" Then
1410                 lRowCt = lRowCt + 1   ' Count Rows associated with Object
1420             End If
1430             lRow = lRow + 1
1440         Loop
1450         Debug.Print "Row: " & lRow & vbTab & "# " & strObjKey & ": " & lRowCt
1460         Sheets("Sheet2").Range("B" & lOutRow) = lRowCt
1470         Sheets("Sheet2").Range("C" & lOutRow) = strObjNameFound
1480         lOutRow = lOutRow + 1
1490     Else
1500         blnSkip = True
1510         lRow = lRow + 1
1520     End If
Next_Row:
1530  Next lRow

1540  Exit Sub

Error_Trap:
    Debug.Print Err.Number & vbTab & Err.Description & vbCrLf & _
            "At Line: " & Erl & vbCrLf & _
            "lLastRow = " & lLastRow & vbTab & "lRow = " & lRow
    MsgBox "Error: " & Err.Number & vbTab & Err.Description & vbCrLf & "At Line: " & Erl & vbCrLf & _
            "lLastRow = " & lLastRow & vbTab & "lRow = " & lRow
    Exit Sub
End Sub

【讨论】:

  • @Wayne 听起来不错。感谢您的耐心等待!一种修改是允许程序通过使用 .Find 搜索其中一个或另一个来在“Objects Cars”或“Objects Books”之间进行选择。也就是说,通过更改在第二个名称下搜索的内容,可以获得“Objects Cars”或“Objects Books”之一的计数
  • (1) 对于该版本,我需要您更新问题以显示“对象”单元格的格式和内容。 (2) 如果搜索到的对象名称(即 Cars)不存在怎么办? (3) 一个人名会有两个以上的对象吗?
  • @Wayne (1) 与上面相同,但“Objects”标题可以命名为“Objects Cars”或“Objects Books”,左对齐且没有空格。 (2) 好吧,他们不会搜索对象名称,而是搜索标题“Object Cars”或“Object Books”。如果标题不存在,则可能会返回错误。 (3) 应该只有 2 个,但请记住,每个(汽车、书籍)只有一个,并且它们永远不会被计算在一起,因为它们是单独的类别。我认为这里的部分混淆可能来自我对“对象”这个词的选择。
  • Stack Overflow 在实时调试时效果不佳,如果您想实时调试或咨询,建议您将其带到聊天室。
  • @George Stocker - 好主意!秒表,我发布了我希望是最终更改的内容 - 您需要在第 35 行指定要搜索的对象类型。如果还有问题,我们一起聊聊。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-03
  • 2022-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多