【问题标题】:Can extract each occurrence of a group individually but not as a repeating group可以单独提取组的每个出现,但不能作为重复组
【发布时间】:2019-01-14 23:23:40
【问题描述】:

我有许多文件的最后一部分是版本号。例如:

Xxxxx V2.txt
Xxxxx V2.3.txt
Xxxxx V2.10.txt
Xxxxx V2.10.3.txt

我使用 Regex 提取版本号的各个部分,以便我可以正确排序文件†,因此我可以计算下一个版本号‡。

† 例如:V2.2 在 V2.10 之前,V2.2 在 V2.2.3 之前。

‡ 例如:V2.9 之后的下一个版本是 V2.10。

我可以单独处理每种样式的版本号,但我不能概括为所有样式创建一个正则表达式模式。

Text               Pattern                          Value(s) extracted
Xxxxx V2.txt       Xxxxx V(\d+)\.txt                2
Xxxxx V2.3.txt     Xxxxx V(\d+)\.(\d+)\.txt         2  3
Xxxxx V2.10.3.txt  Xxxxx V(\d+)\.(\d+)\.(\d+)\.txt  2  10  3
Xxxxx V2.10.3.txt  Xxxxx V(\d+){\.(\d+)}*\.txt      No match

我不明白为什么最后一个模式不适用于每种样式的版本号。任何指导表示赞赏。

响应 cmets 的新部分

我希望我的正则表达式模式中有一个简单的错误,我的代码是无关紧要的。我整理了我的测试代码来创建:

Sub CtrlTestCapture()

  Dim Patterns As Variant
  Dim Texts As Variant

  Texts = Array("Xxxxx V12.txt", _
                "Xxxxx V12.3.txt", _
                "Xxxxx V12.4.5.txt", _
                "Xxxxx V12.4.5.3.txt")

  Patterns = Array("Xxxxx V(\d+)\.txt", _
                   "Xxxxx V(\d+)\.(\d+)\.txt", _
                   "Xxxxx V(\d+)\.(\d+)\.(\d+)\.txt", _
                   "Xxxxx V(\d+){\.(\d+)}+\.txt", _
                   "Xxxxx V(\d+)(?:\.(\d+))?(?:\.(\d+))?\.txt" , _
                   "Xxxxx V(\d+)(\.(\d+))*\.txt")

  Call TestCapture(Patterns, Texts)

End Sub
Sub TestCapture(ByRef Patterns As Variant, ByRef Texts As Variant)

  Dim InxM As Long
  Dim InxS As Long
  Dim Matches As MatchCollection
  Dim PatternCrnt As Variant
  Dim RegEx As New RegExp
  Dim SubMatchCrnt As Variant
  Dim TextCrnt As Variant

  With RegEx
    .Global = True         ' Find all matches
    .MultiLine = False     ' Match cannot extend across linebreak
    .IgnoreCase = True

    For Each PatternCrnt In Patterns
     .Pattern = PatternCrnt

      For Each TextCrnt In Texts
        Debug.Print "==========================================="
        Debug.Print "   Pattern: """ & PatternCrnt & """"
        Debug.Print "      Text: """ & TextCrnt & """"
        If Not .test(TextCrnt) Then
          Debug.Print Space(12) & "Text does not match pattern"
        Else
          Set Matches = .Execute(TextCrnt)
          If Matches.Count = 0 Then
            Debug.Print Space(12) & "Match but no captures"
          Else
            For InxM = 0 To Matches.Count - 1
              Debug.Print "-------------------------------------------"
              With Matches(InxM)
                Debug.Print "     Match: " & InxM + 1
                Debug.Print "     Value: """ & .Value & """"
                Debug.Print "    Length: " & .Length
                Debug.Print "FirstIndex: " & .FirstIndex
                For InxS = 0 To .SubMatches.Count - 1
                  Debug.Print "  SubMatch: " & InxS + 1 & " """ & .SubMatches(InxS) & """"
                Next
              End With
            Next
          End If
        End If
      Next
    Next
    Debug.Print "==========================================="

  End With

End Sub

使用此代码,Wiktor Stribiżew 正则表达式模式比我的杂乱代码产生了更好的结果。我将不得不查看我的原始代码以找到我的错误。使用此代码,Wiktor Stribiżew 正则表达式模式的输出为:

===========================================
   Pattern: "Xxxxx V(\d+)(?:\.(\d+))?(?:\.(\d+))?\.txt"
      Text: "Xxxxx V12.txt"
-------------------------------------------
     Match: 1
     Value: "Xxxxx V12.txt"
    Length: 13
FirstIndex: 0
  SubMatch: 1 "12"
  SubMatch: 2 ""
  SubMatch: 3 ""
===========================================
   Pattern: "Xxxxx V(\d+)(?:\.(\d+))?(?:\.(\d+))?\.txt"
      Text: "Xxxxx V12.3.txt"
-------------------------------------------
     Match: 1
     Value: "Xxxxx V12.3.txt"
    Length: 15
FirstIndex: 0
  SubMatch: 1 "12"
  SubMatch: 2 "3"
  SubMatch: 3 ""
===========================================
   Pattern: "Xxxxx V(\d+)(?:\.(\d+))?(?:\.(\d+))?\.txt"
      Text: "Xxxxx V12.4.5.txt"
-------------------------------------------
     Match: 1
     Value: "Xxxxx V12.4.5.txt"
    Length: 17
FirstIndex: 0
  SubMatch: 1 "12"
  SubMatch: 2 "4"
  SubMatch: 3 "5"
===========================================
   Pattern: "Xxxxx V(\d+)(?:\.(\d+))?(?:\.(\d+))?\.txt"
      Text: "Xxxxx V12.4.5.3.txt"
            Text does not match pattern
===========================================

这具有固定数量的捕获,而不是我尝试的可变数量。我还必须弄清楚如何扩展它以处理“12.4.5.3”,这是我见过的最复杂的版本号样式。这并不完美,但绝对是对我当前解决方法的改进。您正在使用我不认识的正则表达式字符,因此我需要仔细研究。

使用上面的代码,Tiw 正则表达式模式产生了这个输出:

===========================================
   Pattern: "Xxxxx V(\d+)(\.(\d+))*\.txt"
      Text: "Xxxxx V12.txt"
-------------------------------------------
     Match: 1
     Value: "Xxxxx V12.txt"
    Length: 13
FirstIndex: 0
  SubMatch: 1 "12"
  SubMatch: 2 ""
  SubMatch: 3 ""
===========================================
   Pattern: "Xxxxx V(\d+)(\.(\d+))*\.txt"
      Text: "Xxxxx V12.3.txt"
-------------------------------------------
     Match: 1
     Value: "Xxxxx V12.3.txt"
    Length: 15
FirstIndex: 0
  SubMatch: 1 "12"
  SubMatch: 2 ".3"
  SubMatch: 3 "3"
===========================================
   Pattern: "Xxxxx V(\d+)(\.(\d+))*\.txt"
      Text: "Xxxxx V12.4.5.txt"
-------------------------------------------
     Match: 1
     Value: "Xxxxx V12.4.5.txt"
    Length: 17
FirstIndex: 0
  SubMatch: 1 "12"
  SubMatch: 2 ".5"
  SubMatch: 3 "5"
===========================================
   Pattern: "Xxxxx V(\d+)(\.(\d+))*\.txt"
      Text: "Xxxxx V12.4.5.3.txt"
-------------------------------------------
     Match: 1
     Value: "Xxxxx V12.4.5.3.txt"
    Length: 19
FirstIndex: 0
  SubMatch: 1 "12"
  SubMatch: 2 ".3"
  SubMatch: 3 "3"
===========================================

也就是说,它似乎总是捕捉到:第一部分,包括点的最后部分和不带点的最后部分。很有希望,但还不够。

第 3 部分

我忽略了对我所寻求的结果进行明确解释的要求。

我在所有重要文件上都使用版本号。我从其他人那里收到包含版本号的文件,其中一些比我的复杂得多。我总是将版本号作为文件名的最后一部分,并且在版本号之前总是有一个“V”。如果我收到不符合我的格式的文件,我会重命名它们。所以我的文件名称如下:

  • Xxxxx VN.xxx
  • Xxxxx VN.N.xxx
  • Xxxxx VN.N.N.xxx
  • Xxxxx VN.N.N.N.xxx

我希望将 N 提取到可变长度数组或集合中,以便可以使用通用例程处理它们。事实上,我已经有了那些通用的例程。这些例程依赖于一些提取 Ns 的杂乱 VBA 代码。我认为使用 Regex 可以让我整理我的代码。

【问题讨论】:

  • 如果您期望一到三个部分,请使用V(\d+)(?:\.(\d+))?(?:\.(\d+))?\.txt。在 VBA 正则表达式中,您无法访问组的所有捕获。
  • @WiktorStribiżew 感谢您的建议,但它对我不起作用。它为文本的每个字符返回一个匹配项,每个匹配项都是空字符串。
  • 你没有展示你的代码,也没有解释你想要得到什么确切的输出。如果您想让某件事对您有用,则需要提供更多详细信息。
  • @WiktorStribiżew 我已经整理了我的测试代码,你的模式现在给出了更好的结果。请参阅我的问题的新部分。
  • Tiv 的建议似乎消失了。我的问题中的新部分演示了该建议模式的输出。

标签: regex excel vba


【解决方案1】:

试试这个正则表达式:

V(\d+(?:\.\d+)*)\.txt$

Group 1 中捕获了所需的版本。您可以使用. 进一步拆分 Group 1 的内容

Click for Demo

代码:

Dim objReg, strFile, objMatches, strVersion, arrVersion
strFile = "Xxxxx V2.3.txt"
Set objReg = New RegExp
objReg.Global = True
objReg.Multiline = True
objReg.Pattern = "V(\d+(?:\.\d+)*)\.txt$"

If objReg.Test(strFile) Then
    Set objMatches = objReg.Execute(strFile)
    strVersion =  objMatches.item(0).submatches.item(0)   'To get the full version number
    arrVersion = Split(strVersion,".")                    'To get each number in the version(stored in array)
End If

正则表达式解释:

  • V(\d+(?:\.\d+)*)\.txt$
  • V - 匹配 V
  • (\d+(?:\.\d+)*) - 匹配出现 1 次以上的数字。在匹配尽可能多的数字后,匹配 0 次或多次出现的点 .,后跟 1 位以上的数字。整场比赛在第 1 组中被捕获,并且是您所需的版本号
  • \.txt - 匹配 .txt
  • $ - 断言行尾。

【讨论】:

  • @TonyDallimore 这不是您要找的吗?
  • 抱歉回复慢。我正在尝试按顺序正确回复每条评论和答案。这是我发现的第一个有效的建议。它将整个版本号提取为单个字符串,我可以轻松地将其与Split 分开。我正在考虑分别捕获版本号的每个部分,但捕获整个版本号可能更方便。 +1 的工作答案。一旦我检查了所有其他建议,我会接受。
  • 其他建议已被删除,因此您没有对手。这不是我正在寻求的答案,但比我正在寻求的答案更好。我记得当我在支持部门时,我会收到一些关于一些晦涩技术的问题。经过多次询问,我发现他们的实际需求非常简单,但他们认为晦涩难懂的技术就是解决方案。
  • 很高兴该解决方案对您有所帮助:)
【解决方案2】:

如果您愿意,这里有一个非正则表达式解决方案。您可以将版本号转换为数字,然后对其进行排序。

Sub GetOrderedList()
    Dim Texts               As Variant
    Dim FileName            As String
    Dim FileArrayList       As Object
    Dim Item                As Variant

    Set FileArrayList = CreateObject("System.Collections.ArrayList")

    Texts = Array("Xxxxx V12.txt", _
                  "Xxxxx V12.3.txt", _
                  "Xxxxx V12.4.5.txt", _
                  "Xxxxx V12.4.5.3.txt")


    For i = LBound(Texts) To UBound(Texts)
        'You get use the FileSystemObject to make this a bit easier
        FileName = Replace(Replace(Split(Texts(i), " ")(UBound(Split(Texts(i), " "))), "V", ""), ".txt", "")
        PeriodPosition = InStr(1, FileName, ".")

        'Convert to a number, then sort
        If PeriodPosition > 0 Then FileName = Left$(FileName, PeriodPosition) & Replace(FileName, ".", "0", PeriodPosition + 1)
        FileArrayList.Add FileName
    Next

    'Sort
    FileArrayList.Sort

    'Print out, ascending order
    For Each Item In FileArrayList
        Debug.Print Item
    Next

End Sub

【讨论】:

  • 感谢您的回答,其中包括一些有趣的想法。它确实对FileArrayList 进行了正确排序,但这只是我的用例之一。目前,我看不到如何开发这种方法来处理我所有的用例。但是,对于有趣的想法 +1。
猜你喜欢
  • 1970-01-01
  • 2019-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-17
  • 1970-01-01
  • 2019-08-07
  • 1970-01-01
相关资源
最近更新 更多