Range.Address 在公式中使用时被截断(字符串太长)

Range.Address Truncated when Used in Formula (String Too Long)

我有循环遍历用户选择范围的第 5 列的代码,如果第 5 列中的当前单元格符合条件,则将同一行中接下来的 10 个单元格添加到范围中。在一切结束时,如果单元格 A1 为空,它会求和最终范围。

Sub SumItems()
   Dim selectedRange As Range
   Set selectedRange = Selection

   Dim sumRange As Range
   Dim i As Long
   For i = 1 To selectedRange.Rows.Count
      If selectedRange(i, 5) = "Yes" Then
         If sumRange Is Nothing Then
            Set sumRange = Range(selectedRange(i, 5).Offset(0, 1), selectedRange(i, 5).Offset(0, 10))
         Else
            Set sumRange = Union(sumRange, Range(selectedRange(i, 5).Offset(0, 1), selectedRange(i, 5).Offset(0, 10)))
         End If
      End If
   Next i

   If Not sumRange Is Nothing Then
      If IsEmpty(Range("A1")) Then
         Range("A1").Formula = "=SUM(" + sumRange.Address(False, False) + ")"
      Else
         MsgBox "Cannot paste in cell A1, it is not empty."
         Exit Sub
      End If
   Else
      MsgBox "No matching rows were found."
      Exit Sub
   End If
End Sub

我是sheet这个运行长的。所以 sumRange 通常是 sheet 中许多不同区域的联合。我注意到一旦 sumRange.Areas.Count 超过 18,Range("A1").Formula 语句就会出现问题。 sumRange.Address(False, False) 字符串在第 18 个区域后被截断。我相信这是因为 .Address 字符串长度太长,它恰好与这个 sheet.

的 18 个区域重合

我开发了一个临时解决方案,但它限制了子例程可以处理的区域数量,而且笨拙且冗长。如果 sumRange 有超过 18 个区域,我设置了临时范围变量将 sumRange 拆分成 IF 语句现在显示如下:

If Not sumRange Is Nothing Then
   If IsEmpty(Range("A1")) Then
      If sumRange.Areas.Count > 18 Then
         Dim k As Long
         Dim tmpSumRange1 As Range
         Dim tmpSumRange2 As Range
         Dim tmpSumRange3 As Range
         ' And so on...
         For k = 1 To sumRange.Areas.Count
            If k <= 18 Then
               If tmpSumRange1 Is Nothing Then
                  Set tmpSumRange1 = sumRange.Areas(k)
               Else
                  Set tmpSumRange1 = Union(tmpSumRange1, sumRange.Areas(k))
               End If
            ElseIf k > 18 And k <= 36 Then
               If tmpSumRange2 Is Nothing Then
                  Set tmpSumRange2 = sumRange.Areas(k)
               Else
                  Set tmpSumRange2 = Union(tmpSumRange2, sumRange.Areas(k))
               End If
            ElseIf k > 36 And k <= 54 Then
               If tmpSumRange3 Is Nothing Then
                  Set tmpSumRange3 = sumRange.Areas(k)
               Else
                  Set tmpSumRange3 = Union(tmpSumRange3, sumRange.Areas(k))
               End If

            ElseIf
               ' And so on until I get to k = 180...
            End If
         Next k

         If Not tmpSumRange1 Is Nothing And tmpSumRange2 Is Nothing And tmpSumRange3 Is Nothing Then
            Range("A1").Formula = "=SUM(" + tmpSumRange1.Address(False, False) + ")"
         ElseIf Not tmpSumRange1 Is Nothing And Not tmpSumRange2 Is Nothing And tmpSumRange3 Is Nothing Then
            Range("A1").Formula = "=SUM(" + tmpSumRange1.Address(False, False) + "," + tmpSumRange2.Address(False, False) + ")"
         ElseIf Not tmpSumRange1 Is Nothing And tmpSumRange2 Is Nothing And Not tmpSumRange3 Is Nothing Then
            Range("A1").Formula = "=SUM(" + tmpSumRange1.Address(False, False) + "," + tmpSumRange2.Address(False, False) + "," + tmpSumRange3.Address(False, False) + ")"
         ElseIf
            ' And so on all the way up to tmpSumRange10...
         End If
      Else
         Range("A1").Formula = "=SUM(" + sumRange.Address(False, False) + ")"
      End If
   Else
      MsgBox "Cannot paste in cell A1, it is not empty."
      Exit Sub
   End If
Else
   MsgBox "No matching rows were found."
   Exit Sub
End If

我知道必须有比这更好的解决方案。我在搜索中没有找到任何东西。我也没有找到遇到过同样问题的人。我需要 A1 单元格具有 SUM 公式,其中包含范围,否则我将只对子例程中的值求和并插入值而不是公式。

所以有两个独立但可能相关的问题:

  • Range.Address 最多截断 255 个字符,没有错误。这很奇怪,因为虽然 Excel 足够聪明,不会给你留下会引发 1004 的部分地址字符串,但同时它又愚蠢到允许 Address 截断,这显然会如果用于公式等会产生意想不到的结果
  • 如果将字符串传递给 ByVal(参见 this old thread)以供讨论,Excel 函数的字符串参数有 255 个字符的限制,但我已经摘录了相关内容位,下面)。

Excel function parameters will accept a reference to a string with length of up to 32767 (32k or 2^15 total possible lengths). ... But what if we pass a value instead of a reference?

we can pass a reference to a string up to 32k, we can only pass a string value up to 255

问题似乎是 Formula 长度的结果,但这是一个转移注意力的问题。

您肯定遇到了 Range.Address 的截断问题。

问题:Range.Address 在 255 个字符处截断

由于这种情况悄无声息地发生,因此您基于截断地址构建的任何公式都会受到不利影响;结果不会是您所期望的,因为公式不会代表范围的所有部分!

我已将此记录为 Microsoft 的错误,以防您想关注此问题:

https://github.com/MicrosoftDocs/VBA-Docs/issues/49

同时,幸好解决方案非常简单(虽然不是很容易得出!),看看你能不能适应你的目的,但思路是:

  1. 创建一个 System.Collections.ArrayList,我们将所有单独的 Area 地址存储在其中(或者您可以使用 Variant,但是 ReDim等等)
  2. 使用 ToArray() 方法将 ArrayList 转换为变体数组
  3. Formula 字符串中使用 Join 函数的结果

Sub example()

Dim myNames As Object
Dim formula as String
Dim k As Long
Dim sumRange As Range
Dim thisArea As Range

' Example:
'   -- creates a range consisting of 100 Areas
'   -- this .Address will truncate to 252 characters!
For k = 1 To 200 Step 2
    Set thisArea = Range("A" & k)
    If sumRange Is Nothing Then Set sumRange = thisArea
    Set sumRange = Union(sumRange, thisArea)
Next

' Iterate the sumRange union and add each Area to an ArrayList
Set myNames = CreateObject("System.Collections.ArrayList")
For Each thisArea In sumRange.Areas
    myNames.Add thisArea.Address
Next

' Output to worksheet:
Dim outputCell As Range
Set outputCell = Range("F1")
formula = "=Sum(" & Join(myNames.ToArray(), ",") & ")"
outputCell.Formula = formula

End Sub

并且可以在工作表中看到特别长的公式(Len == 650):

如果您不习惯使用 ArrayList,您可以将其强制为 `String:

' Iterate the sumRange union and add each Area to our formula string
formula = "=Sum("
For Each thisArea In sumRange.Areas
    formula = formula & thisArea.Address & ","
Next
formula = Left(formula, Len(formula) - 1)
formula = formula & ")"