我有一个 VBA 源代码,其中包含许多对单元格的硬编码引用。该代码是 Worksheet_Change
的一部分sub,所以我想对范围引用进行硬编码是必要的,您将看到许多赋值语句,如下所示:
Set cell = Range("B7")
If Not Application.Intersect(cell, Range(Target.Address)) Is Nothing Then
我想在工作表顶部插入 2 行,因此基本上所有行引用都会移动 2 行。例如上面的赋值语句将改为 Set cell = Range("B9")
.
鉴于代码中存在大量硬编码的行引用,我想到使用 Regex 将所有行引用增加 2。因此我开发了以下代码。
Sub UpdateVBACode()
'*********************Read Text File Containing VBA code and assign content to string variable*************************
Dim str As String
Dim strFile As String: strFile = "F:\Preprocessed_code.txt"
Open strFile For Input As #1
str = Input$(LOF(1), 1)
Close #1
'*********************Split string variables to lines******************************************************************
Dim vStr As Variant: vStr = Split(str, vbCrLf)
'*********************Regex work***************************************************************************************
Dim rex As New RegExp
rex.Global = True
Dim i As Long
Dim mtch As Object
rex.Pattern = "(""\w)([0-9][0-9])("")" ' 3 capturing groups to reconstruct the replacement string
For i = 0 To UBound(vStr, 1)
If rex.Test(vStr(i)) Then
For Each mtch In rex.Execute(vStr(i))
vStr(i) = rex.Replace(vStr(i), mtch.SubMatches(0) & IncrementString(mtch.SubMatches(1)) & mtch.SubMatches(2))
Next
End If
Next i
'********************Reconstruct String*********************************************************************************
str = ""
For i = 0 To UBound(vStr, 1)
str = str & vbCrLf & vStr(i)
Next i
'********************Write string to text file******************************************************************************
Dim myFile As String
myFile = "F:\Processed_code.txt"
Open myFile For Output As #2
Print #2, str
Close #2
'
End Sub
Function IncrementString(rowNum As String) As String '
Dim num As Integer
num = CInt(rowNum) + 2
IncrementString = CStr(num)
End Function
上面的 VBA 代码可以工作,但如果同一行中有两个行引用,则该代码会失败,例如,如果我们有 If Range("B15").Value <> Range("B12").Value Then
,处理该行后我得到 If Range("B14").Value <> Range("B14").Value Then
而不是If Range("B17").Value <> Range("B14").Value Then
。问题出在 vStr(i) = rex.Replace(vStr(i), mtch.SubMatches(0) & IncrementString(mtch.SubMatches(1)) & mtch.SubMatches(2))
语句,因为如果一行有多个正则表达式匹配,它就会被多次调用。
有什么想法吗?提前致谢
最佳答案
我认为您尝试做的事情是一个坏主意,原因有两个:
- 硬编码的单元格引用几乎总是不好的做法。更好的解决方案可能是用命名范围替换硬编码的单元格引用。您可以在代码中按名称引用它们,如果插入/删除行或列,关联的引用将自动更新。您需要做一些痛苦的前期工作,但结果将是一个更易于维护的电子表格。
- 您实际上正在尝试使用正则表达式编写 VBA 解析器。这几乎肯定不会在所有情况下都起作用。您当前的正则表达式将匹配许多不是单元格引用的内容(例如
"123"
、"_12"
和"A00"
),并且还会丢失许多硬编码单元格引用(例如"A1"
和Cell(3,7)
) 。这对于您的特定代码可能并不重要,但确保其有效的唯一方法是手动检查每个引用。恕我直言,这比重构(例如用命名范围替换)的工作量要少得多。根据我的经验,您不会修复正则表达式,只会使问题变得更加微妙。
也就是说,既然你问了......
<cthulu>
使用RegExp.Replace()
时只有两个选择- 替换第一个匹配项或替换所有匹配项(分别对应于将 RegExp.Global
设置为 False
或 True
)。你没有比这更好的控制,所以你的逻辑必须改变。而不是使用 Replace()
您可以使用 FirstIndex
编写自己的替换代码Match
的属性(property)对象,以及 VBA 的字符串函数来隔离字符串的相关部分:
Dim rex As Object
Set rex = CreateObject("VBScript.RegExp")
rex.Global = True
Dim i As Long
Dim mtch As Object
Dim newLineText As String
Dim currMatchIndex As Long, prevPosition As Long
rex.Pattern = "(""\w)([0-9][0-9])("")" ' 3 capturing groups to reconstruct the replacement string
For i = 0 To UBound(vStr, 1)
If rex.Test(vStr(i)) Then
currMatchIndex = 0: prevPosition = 1
newLineText = ""
For Each mtch In rex.Execute(vStr(i))
'Note that VBA string functions are indexed from 1 but Match.FirstIndex starts from 0
currMatchIndex = mtch.FirstIndex
newLineText = newLineText & Mid(vStr(i), prevPosition, currMatchIndex - prevPosition + 1) & _
mtch.SubMatches(0) & IncrementString(mtch.SubMatches(1)) & mtch.SubMatches(2)
prevPosition = currMatchIndex + Len(mtch.Value) + 1
Next
vStr(i) = newLineText & Right(vStr(i), Len(vStr(i)) - prevPosition + 1)
End If
Next i
请注意,我仍然没有首先解决正则表达式模式的问题。我建议您直接使用命名范围...
哎呀,差点忘了 - </cth
关于regex - 使用 Regex 更新 VBA 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27531333/