这是RegEx用于匹配Excel公式中的任何单元格引用吗?

问题描述 投票:5回答:2

我一直在尝试创建一个正则表达式模式,匹配任何Excel公式中的任何引用,包括绝对,相对和外部引用。我需要返回整个引用,包括工作表和工作簿名称。

我无法找到有关Excel A1表示法的详尽文档,但经过大量测试后我确定了以下内容:

  • 公式前面带有等号“=”
  • 公式中的字符串用双引号括起来,在查找实际引​​用之前需要删除,否则=A1&"A1"会破坏正则表达式
  • 工作表名称最长可达31个字符,不包括\ /? * []:
  • 外部引用中的工作表名称必须使用bang =Sheet1!A1成功
  • 外部引用中的工作簿名称必须用方括号=[Book1.xlsx]Sheet1!A1括起来
  • 如果引用是封闭工作簿中的范围,则Excel添加的工作簿路径始终用单引号括起来,并在工具簿名称'C:\[Book1.xlsx]Sheet1'!A1的括号左侧
  • 某些字符(例如,不间断的空格)导致Excel将工作簿和工作表名称用单引号括在外部引用中,但我不知道具体哪些字符='[Book 1.xlsx]Sheet 1'!A1
  • 即使启用了R1C1表示法,Range.Formula仍会以A1表示法返回引用。 Range.FormulaR1C1返回R1C1表示法中的引用。
  • 3D参考样式允许在一个工作簿=SUM([Book5]Sheet1:Sheet3!A1)上使用一系列工作表名称
  • 命名范围可以在公式中指定:
    • 名称的第一个字符必须是字母,下划线字符(_)或反斜杠(\)。名称中的剩余字符可以是字母,数字,句点和下划线字符。
    • 您不能将大写和小写字符“C”,“c”,“R”或“r”用作已定义的名称,因为它们都用作选择当前所选单元格的行或列的简写。在名称或转到文本框中输入它们。
    • 名称不能与单元格引用相同,例如Z $ 100或R1C1。
    • 空格不允许作为名称的一部分。
    • 名称最长可达255个字符。
    • 名称可以包含大写和小写字母。 Excel不区分名称中的大写和小写字符。

以下是我提出的用于测试的VBA程序。我更新了代码以处理名称:

Sub ReturnFormulaReferences()

    Dim objRegExp As New VBScript_RegExp_55.RegExp
    Dim objCell As Range
    Dim objStringMatches As Object
    Dim objReferenceMatches As Object
    Dim objMatch As Object
    Dim intReferenceCount As Integer
    Dim intIndex As Integer
    Dim booIsReference As Boolean
    Dim objName As Name
    Dim booNameFound As Boolean

    With objRegExp
        .MultiLine = True
        .Global = True
        .IgnoreCase = True
    End With

    For Each objCell In Selection.Cells
        If Left(objCell.Formula, 1) = "=" Then

            objRegExp.Pattern = "\"".*\"""
            Set objStringMatches = objRegExp.Execute(objCell.Formula)

            objRegExp.Pattern = "(\'.*(\[.*\])?([^\:\\\/\?\*\[\]]{1,31}\:)?[^\:\\\/\?\*\[\]]{1,31}\'\!" _
            & "|(\[.*\])?([^\:\\\/\?\*\[\]]{1,31}\:)?[^\:\\\/\?\*\[\]]{1,31}\!)?" _
            & "(\$?[a-z]{1,3}\$?[0-9]{1,7}(\:\$?[a-z]{1,3}\$?[0-9]{1,7})?" _
            & "|\$[a-z]{1,3}\:\$[a-z]{1,3}" _
            & "|[a-z]{1,3}\:[a-z]{1,3}" _
            & "|\$[0-9]{1,7}\:\$[0-9]{1,7}" _
            & "|[0-9]{1,7}\:[0-9]{1,7}" _
            & "|[a-z_\\][a-z0-9_\.]{0,254})"
            Set objReferenceMatches = objRegExp.Execute(objCell.Formula)

            intReferenceCount = 0
            For Each objMatch In objReferenceMatches
                intReferenceCount = intReferenceCount + 1
            Next

            Debug.Print objCell.Formula
            For intIndex = intReferenceCount - 1 To 0 Step -1
                booIsReference = True
                For Each objMatch In objStringMatches
                    If objReferenceMatches(intIndex).FirstIndex > objMatch.FirstIndex _
                    And objReferenceMatches(intIndex).FirstIndex < objMatch.FirstIndex + objMatch.Length Then
                        booIsReference = False
                        Exit For
                    End If
                Next

                If booIsReference Then
                    objRegExp.Pattern = "(\'.*(\[.*\])?([^\:\\\/\?\*\[\]]{1,31}\:)?[^\:\\\/\?\*\[\]]{1,31}\'\!" _
                    & "|(\[.*\])?([^\:\\\/\?\*\[\]]{1,31}\:)?[^\:\\\/\?\*\[\]]{1,31}\!)?" _
                    & "(\$?[a-z]{1,3}\$?[0-9]{1,7}(\:\$?[a-z]{1,3}\$?[0-9]{1,7})?" _
                    & "|\$[a-z]{1,3}\:\$[a-z]{1,3}" _
                    & "|[a-z]{1,3}\:[a-z]{1,3}" _
                    & "|\$[0-9]{1,7}\:\$[0-9]{1,7}" _
                    & "|[0-9]{1,7}\:[0-9]{1,7})"
                    If Not objRegExp.Test(objReferenceMatches(intIndex).Value) Then 'reference is not A1
                        objRegExp.Pattern = "^(\'.*(\[.*\])?([^\:\\\/\?\*\[\]]{1,31}\:)?[^\:\\\/\?\*\[\]]{1,31}\'\!" _
                        & "|(\[.*\])?([^\:\\\/\?\*\[\]]{1,31}\:)?[^\:\\\/\?\*\[\]]{1,31}\!)" _
                        & "[a-z_\\][a-z0-9_\.]{0,254}$"
                        If Not objRegExp.Test(objReferenceMatches(intIndex).Value) Then 'name is not external
                            booNameFound = False
                            For Each objName In objCell.Worksheet.Parent.Names
                                If objReferenceMatches(intIndex).Value = objName.Name Then
                                    booNameFound = True
                                    Exit For
                                End If
                            Next
                            If Not booNameFound Then
                                objRegExp.Pattern = "^(\'.*(\[.*\])?([^\:\\\/\?\*\[\]]{1,31}\:)?[^\:\\\/\?\*\[\]]{1,31}\'\!" _
                                & "|(\[.*\])?([^\:\\\/\?\*\[\]]{1,31}\:)?[^\:\\\/\?\*\[\]]{1,31}\!)"
                                For Each objName In objCell.Worksheet.Names
                                    If objReferenceMatches(intIndex).Value = objRegExp.Replace(objName.Name, "") Then
                                        booNameFound = True
                                        Exit For
                                    End If
                                Next
                            End If
                            booIsReference = booNameFound
                        End If
                    End If
                End If

                If booIsReference Then
                    Debug.Print "  " & objReferenceMatches(intIndex).Value _
                    & " (" & objReferenceMatches(intIndex).FirstIndex & ", " _
                    & objReferenceMatches(intIndex).Length & ")"
                End If
            Next intIndex
            Debug.Print

        End If
    Next

    Set objRegExp = Nothing
    Set objStringMatches = Nothing
    Set objReferenceMatches = Nothing
    Set objMatch = Nothing
    Set objCell = Nothing
    Set objName = Nothing

End Sub

谁能打破或改善这个?如果没有关于Excel公式语法的详尽文档,很难知道这是否正确。

谢谢!

regex excel vba excel-formula
2个回答
3
投票

jtolle引导我朝着正确的方向前进。据我所知,这就是我试图做的事情。我一直在测试它似乎工作。

stringOriginFormula = rangeOrigin.Formula
rangeOrigin.Cut rangeDestination
rangeOrigin.Formula = stringOriginFormula

谢谢jtolle!


0
投票

谢谢Ben(我很想在这里发帖,尽管Stackoverflow多年来引起了我对高质量技术的关注,所以我不确定我是否正确地为作者J阅读了这个页面)

我尝试了发布的解决方案(测试,测试更新,以及使用range.precendents(正确指出,不包括对其他工作表或其他工作簿的引用)的解决方案,并发现一个小缺陷:外部工作表名称包含在'单引号'只有在它是一个数字时;如果它包含空格(可能还有其他字符如Ben(?)在原始帖子中列出。只需加上regEx(开[],就可以更正(添加“ [“,请参阅下面的代码。)此外,为了我自己的目的,我将sub转换为一个函数,它将返回一个逗号分隔的列表,删除重复项(注意,这将删除相同的引用符号,而不是包含在多个中的单元格范围):

Public Function CellReflist(Optional r As Range)  ' single cell
Dim result As Object: Dim testExpression As String: Dim objRegEx As Object
If r Is Nothing Then Set r = ActiveCell ' Cells(1, 2)  ' INPUT THE CELL HERE , e.g.    RANGE("A1")
Set objRegEx = CreateObject("VBScript.RegExp")
objRegEx.IgnoreCase = True: objRegEx.Global = True: objRegEx.Pattern = """.*?"""    ' remove expressions
testExpression = CStr(r.Formula)
testExpression = objRegEx.Replace(testExpression, "")
'objRegEx.Pattern = "(([A-Z])+(\d)+)"  'grab the address

objRegEx.Pattern = "(['\[].*?['!])?([[A-Z0-9_]+[!])?(\$?[A-Z]+\$?(\d)+(:\$?[A-Z]+\$?(\d)+)?|\$?[A-Z]+:\$?[A-Z]+|(\$?[A-Z]+\$?(\d)+))"
If objRegEx.Test(testExpression) Then
    Set result = objRegEx.Execute(testExpression)
    If result.Count > 0 Then CellReflist = result(0).Value
    If result.Count > 1 Then
        For i = 1 To result.Count - 1 'Each Match In result
            dbl = False ' poistetaan tuplaesiintymiset
            For j = 0 To i - 1
                If result(i).Value = result(j).Value Then dbl = True
            Next j
            If Not dbl Then CellReflist = CellReflist & "," & result(i).Value 'Match.Value
        Next i 'Match
    End If
End If

结束功能

© www.soinside.com 2019 - 2024. All rights reserved.