使用Regex更新VBA代码

时间:2014-12-17 17:26:04

标签: regex vba excel-vba excel

我有一个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而不是{ {1}}。问题出在If Range("B17").Value <> Range("B14").Value Then语句中,因为如果一行超过Regex匹配,它会被多次调用。

有什么想法吗?提前致谢

1 个答案:

答案 0 :(得分:2)

我认为你想要做的是一个坏主意,原因有两个:

  1. 硬编码的细胞参考几乎总是不好的做法。更好的解决方案可能是用命名范围替换硬编码的单元格引用。您可以按名称在代码中引用它们,如果插入/删除行或列,相关引用将自动更新。您需要做一些痛苦的前期工作,但结果将是一个更易于维护的电子表格。
  2. 您正在尝试使用正则表达式编写VBA解析器。这几乎保证不会在所有情况下都起作用。您当前的正则表达式将匹配许多不是单元格引用的内容(例如"123""_12""A00"),并且还会遗漏许多硬编码的单元格引用(例如"A1"Cell(3,7))。这可能对您的特定代码无关紧要,但确保其有效的唯一方法是手动检查每个引用。这是恕我直言,而不是重构(例如,用命名范围替换)。根据我的经验,你不会修复正则表达式,只是让问题更加微妙。
  3. 那说,既然你问过......

    <cthulu>

    使用RegExp.Replace()时只有两种选择 - 替换第一场比赛或替换所有匹配(分别对应于RegExp.GlobalFalse设置)。你没有比那更精细的控制,所以你的逻辑必须改变。您可以使用True对象的Replace()属性和VBA的字符串函数来编写自己的替换代码,而不是使用FirstIndex来隔离相关的部分。字符串:

    Match

    请注意,我仍然没有解决正则表达式模式的问题。我建议您只使用命名范围...

    哎呀,差点忘了 - 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