【问题标题】:How to build non-consecutive ranges of rows based on cell contents?如何根据单元格内容构建不连续的行范围?
【发布时间】:2013-07-20 10:20:40
【问题描述】:

我刚刚开始使用 VBA for Excel。近十年前,我在大学里使用过 VB 和 Java,那时我已经能胜任了,但基本上是从头开始。 (嗯,喜欢骑自行车。)

我正在尝试了解构建不只是声明为 A1:J34 或其他内容的范围的方法。我的谷歌搜索遇到了挑战,因为在搜索“范围”和表明我所寻求的术语时,我得到了比我需要的更高级的点击量,大多数点击量甚至没有解决我需要的基本摘要信息。

所以,这里是它的基础知识: Mac 上的 Excel 2011。 工作表的数据从 A 到 M,一直到 1309。 这是标题行后跟数据行的重复模式。啊。似乎创建工作表的人更多地考虑从工作表打印而不是数据的组织。我需要清理它和另外 3 个类似的东西才能在数据透视表中使用,而它在这种愚蠢的重复布局中毫无用处。

标题行如下: 姓氏、名字,然后是 10 个日期单元格。 标题下的数据行当然是姓名,然后是 1 或 0 代表出勤率。 每个标题下有 20 到 30 个名称。然后它重复。并且每隔几组日期就会改变一次,从上一组停止的地方开始。

我现在需要做的事情: 我正在尝试通过添加以特定值开头的所有行(在 A 列中)来将一个范围组合成一个范围变量。在我的情况下,该值是字符串“姓氏”,因此我可以让范围变量保存所有以“姓氏”开头的行中的所有单元格。然后,这将捕获所有需要采用日期格式的单元格。 (我这样做是为了确保日期标题实际上都是日期格式 - 因为它们现在不都是日期格式,许多只是“常规”单元格。)

我的问题:

  1. 当告诉一个范围对象它的范围是什么时,如何向它提供单元格/行/列,这些单元格/行/列不仅仅是由编写代码的人输入的开始和结束单元格定义的块,而是基于行标准?例如:根据 A 中“名字”的存在,创建一个 Range,其中包含从 A 列到 I 列的第 1、34、70、93 和 128 行。
  2. 最常用的方法是什么?
  3. 其中哪一个最适合我的需要?为什么?

【问题讨论】:

    标签: vba excel excel-2011


    【解决方案1】:

    这是一个工作示例,演示如何查找“姓氏”行,构造一个包含所有这些行的范围对象,然后遍历该对象以搜索非日期值。通过将数据范围读取到变量数组中,然后在数组中搜索姓氏行和这些行中的“错误日期”,可以大大加快代码速度。如果要检查的行数非常多,则尤其如此。

    Sub DisjointRng()
    
        Dim checkCol As String, checkPattern As String
        Dim dateCols()
        Dim lastCell As Range, usedRng As Range, checkRng As Range
        Dim cell As Variant
        Dim usedRow As Range, resultRng As Range, rngArea As Range
        Dim i As Long, j As Long
    
        checkCol = "A"             'column to check for "Last Name"
        checkPattern = "Last*"
        dateCols = Array(3, 5)     'columns to check for date formatting
    
        With Worksheets("Sheet1")
            'find the bottom right corner of data range; we determine the used range
                'ourselves since the built-in UsedRange is sometimes out-of-synch
            Set lastCell = .Cells(.Cells.Find(What:="*", SearchOrder:=xlRows, _
                SearchDirection:=xlPrevious, LookIn:=xlFormulas).Row, _
                .Cells.Find(What:="*", SearchOrder:=xlByColumns, _
                SearchDirection:=xlPrevious, LookIn:=xlFormulas).Column)
            Set usedRng = .Range("A1:" & lastCell.Address)
            'the column of values in which to look for "Last Name"
            Set checkRng = .Range(checkCol & "1:" & checkCol & usedRng.Rows.Count)
        End With
        'step down the column of values to check for last name & add
            'add found rows to range object
        For Each cell In checkRng
            If cell.Value Like checkPattern Then
               'create a range object for the row
                Set usedRow = Intersect(cell.EntireRow, usedRng)
                If resultRng Is Nothing Then
                    'set the first row with "Last Name"
                    Set resultRng = usedRow
                Else
                    'add each additional found row to the result range object
                    Set resultRng = Union(resultRng, usedRow)
                End If
            End If
        Next cell
        For Each rngArea In resultRng.Areas
            'if found rows are continguous, Excel consolidates them
                'into single area, so need to loop through each of the rows in area
            For i = 1 To rngArea.Rows.Count
                For j = LBound(dateCols) To UBound(dateCols)
                    If Not IsDate(rngArea.Cells(i, dateCols(j))) Then
                        'do something
                    End If
                Next j
            Next i
        Next rngArea
    End Sub
    

    【讨论】:

    • 太棒了,谢谢! 1) dateCols 变量设置是否通过 3、4 和 5 的数组中的位置指示 C、D 和 E 列?似乎很明显,但想要确认(菜鸟),所以我可以将其更改为 N。2)“.Cells.Find(什么:=”部分中的通配符(*) - 这是在做什么?3)你让我在嵌套 Fors 内的非日期单元格上编写一个动作 - 为什么是这种情况,而不是外部 For 关闭后的 rngArea.whatever 语句? (只是试图理解逻辑而不仅仅是代码)再次谢谢你。在代码方面超出了我的预期。
    • 顺便说一句,我会给 +1,但我在这里是全新的,还不能投票。
    • 当然,如果您愿意,您可以接受答案。 1) 是的,3, 4, 5 是列号 2) Find 中的 * 是通配符 - 语句说“查找包含任何内容的单元格” 3) 我假设您想在每个单元格上执行一些操作的非格式化日期单元格。外部For循环遍历resultRng中的区域,可能看起来像(A3:X3,A7:X14,A15:X15等);第二个For包含1行以上的块(区域)的行,例如A7:X14;内部For 一行中的每个单元格。如果 rngResult 只是单个行的集合,您只需要 2 个 For's
    【解决方案2】:

    您可以像这样使用Union 运算符

    Dim r As Range
    
    Set r = Range("A1, A3, A10:A12")
    

    或者这个

    Set r = Union(Range("A1"), Range("A3"), Range("A10:A12"))
    

    你可以像这样迭代这个范围

    Dim cl as Range
    For Each cl in r.Cells
        ' code cell cl
    Next
    

    或者这个

    Dim ar as Range
    For each ar in r.Areas
        ' code using contiguous range ar
        For each cl in ar.Cells
            ' code using cell cl
        Next
    Next
    

    【讨论】:

    • 谢谢,克里斯,但不是我想要的。在上面,您明确命名定义范围的单元格。我想知道如何通过扫描工作表以查找完整行来定义范围的单元格,但如果该行的第一个单元格与设定的条件匹配,则仅将它们包含在范围的范围内。我将不得不谷歌这个 .Cells 是什么。记住,我是菜鸟!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-12
    • 1970-01-01
    • 2021-10-16
    • 2013-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多