【问题标题】:Optimal means of obtaining cell address column letter from column index and column index from column letter从列索引中获取单元格地址列字母和从列字母中获取列索引的最优方法
【发布时间】:2021-05-05 17:24:20
【问题描述】:

通常接受的方法是执行以下操作

数字到字母

public function numberToLetter(ByVal i as long) as string
  Dim s as string: s = cells(1,i).address(false,false)
  numberToLetter = left(s,len(s)-1)
end function

给数字的信

Public Function letterToNumber(ByVal s As String) As Long
  letterToNumber = Range(s & 1).Column
End Function

然而,这些都不是特别理想的,因为在每种情况下,我们都在创建一个对象,然后在该对象上调用一个属性访问器。有更快的方法吗?

【问题讨论】:

  • 您没有创建对象 - 在这两种情况下,单元格都已经存在。如果您确实需要在列字母和索引之间进行映射,那么这对我来说似乎很好:如果您尝试映射不存在的列,则会出错。
  • @TimWilliams 我很确定 range(...) amd cells(...) 会创建一个对象,或者更确切地说,我很确定不会为您打开的每个电子表格创建一些 2147483647 对象(一个用于整个电子表格中的每个单元格),因为这会非常慢。
  • 好的,公平点...

标签: excel vba indexing letter


【解决方案1】:

总结

要实现的核心是Excel中使用的刻字系统也称为Base26。 NumberToLetter 是从十进制编码到 Base26,LetterToNumber 是从 Base26 解码到十进制。

基础转换可以通过简单的循环和

Function base26Encode(ByVal iDecimal As Long) As String
  if iDecimal <= 0 then Call Err.Raise(5, "base26Encode" ,"Argument cannot be less than 0")
  if iDecimal >= 16384 then Call Err.Raise(5, "base26Encode" ,"There are only 16384 columns in a spreadsheet, thus this function is limited to this number.")
  Dim s As String: s = ""
  Do
    Dim v As Long
    v = (iDecimal - 1) Mod 26 + 1
    iDecimal = (iDecimal - v) / 26
    s = Chr(v + 64) & s
  Loop Until iDecimal = 0
  base26Encode = s
End Function

Function base26Decode(ByVal sBase26 As String) As Long
  sBase26 = UCase(sBase26)
  Dim sum As Long: sum = 0
  Dim iRefLen As Long: iRefLen = Len(sBase26)
  For i = iRefLen To 1 Step -1
    sum = sum + (Asc((Mid(sBase26, i))) - 64) * 26 ^ (iRefLen - i)
  Next
  base26Decode = sum
End Function

性能

我针对原始函数测试了这些函数的性能。为此,我使用了 stdVBA 的 stdPerformance 类。

用于测试的代码如下:

Sub testPerf()
  Dim cMax As Long: cMax = 16384
  With stdPerformance.Measure("Encode Original")
    For i = 1 To cMax
      Call numberToLetter(i)
    Next
  End With
  With stdPerformance.Measure("Encode Optimal")
    For i = 1 To cMax
      Call base26Encode(i)
    Next
  End With
  With stdPerformance.Measure("Decode Original")
    For i = 1 To cMax
      Call letterToNumber(base26Encode(i))
    Next
  End With
  With stdPerformance.Measure("Decode Optimal")
    For i = 1 To cMax
      Call base26Decode(base26Encode(i))
    Next
  End With
End Sub

结果如下:

Encode Original: 78 ms
Encode Optimal: 31 ms
Decode Original: 172 ms
Decode Optimal: 63 ms

如图所示,这是一种稍快的方法(快 2-3 倍)。然而,我对对象创建和属性访问的表现如此之好感到相当惊讶。

【讨论】:

  • 而不是v = iDecimal Mod 26 & If v = 0 Then v = 26 使用v = (iDecimal - 1) Mod 26 + 1,它具有相当的速度但更简洁的代码。此外,如果您将 0 传递给函数,您会得到一个奇怪的字符串,因此对此进行错误检查会很好。
猜你喜欢
  • 1970-01-01
  • 2020-12-01
  • 2011-01-28
  • 1970-01-01
  • 1970-01-01
  • 2014-02-06
  • 2013-03-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多