VBScript正则表达式填充子匹配即使不匹配也是必需的

时间:2014-02-12 23:58:48

标签: regex excel vba excel-vba

我正在尝试复制Google日历从叙述中创建约会的方法。我想输入5pm Happy Hour for 1 hour并将其解析为最终的Outlook AppointmentItem。

我认为,我的问题是,最后我有一大块可选文字。并且因为它是可选的,所以正则表达式通过但子匹配不会被填充,因为匹配不需要它。我希望它填充,因为我想使用子匹配作为我的解析引擎。

我在A列中有一堆测试用例(在Excel中工作,然后将移动到Outlook),我的代码列出了右边的子匹配。这是潜在输入的代表性样本

1. 5pmCST Happy Hour for 1 hour
2. 5pm CST Happy Hour for 1 hour
3. 5pm Happy Hour for 1 hour
4. 5 pm Happy Hour for 1 hour
5. 5 pm CST Happy Hour for 1 hour
6. 5 Happy Hour for 1 hour
7. 5 Happy Hour
8. 5pmCST Happy Hour
9. 5pm CST Happy Hour
10. 5pm Happy Hour
11. 5:00CST Happy Hour for 1 hour
12. 5:00 CST Happy Hour for 1 hour

这是运行测试的代码

Sub testest()

    Dim RegEx As VBScript_RegExp_55.RegExp
    Dim Matches As VBScript_RegExp_55.MatchCollection
    Dim Match As VBScript_RegExp_55.Match
    Dim rCell As Range
    Dim SubMatch As Variant
    Dim lCnt As Long
    Dim aPattern(1 To 8) As String

    Set RegEx = New VBScript_RegExp_55.RegExp
    aPattern(1) = "(1?[0-9](:[0-5][0-9])?)" 'time
    aPattern(2) = "( ?)" 'optional space
    aPattern(3) = "([ap]m)?" 'optional ampm
    aPattern(4) = "( ?)" 'optional space
    aPattern(5) = "([ECMP][DS]T)?" 'optional time zone
    aPattern(6) = "( ?)" 'optional space
    aPattern(7) = "(.+?)" 'event description
    aPattern(8) = "(( for )([1-2]?[0-9](.[0-9]?[0-9])?)( hours?))?" 'optional duration

    RegEx.Pattern = Join(aPattern, vbNullString)
    Debug.Print RegEx.Pattern

    Sheet1.Range("C1").Resize(1000, 100).ClearContents

    For Each rCell In Sheet1.Range("A1").CurrentRegion.Columns(1).Cells
        lCnt = 0
        rCell.Offset(0, 2).Value = RegEx.test(rCell.Text)
        If RegEx.test(rCell.Text) Then
            Set Matches = RegEx.Execute(rCell.Text)

            For Each Match In Matches
                For Each SubMatch In Match.SubMatches
                    lCnt = lCnt + 1
                    rCell.Offset(0, 2 + lCnt).Value = SubMatch
                Next SubMatch
            Next Match
        End If
    Next rCell

End Sub

模式是

(1?[0-9](:[0-5][0-9])?)( ?)([ap]m)?( ?)([ECMP][DS]T)?( ?)(.+?)(( for )([1-2]?[0-9](.[0-9]?[0-9])?)( hours?))?

#1的子匹配是

1        2          3        4      5       6       7
5                   pm              CST             H

它在欢乐时光中的“H”处停止匹配,因为以“for”开头的所有内容都是可选的。如果我删除了可选部分,我的模式将变为

(1?[0-9](:[0-5][0-9])?)( ?)([ap]m)?( ?)([ECMP][DS]T)?( ?)(.+?)( for )([1-2]?[0-9](.[0-9]?[0-9])?)( hours?)

但是#7-#10没有通过,因为他们没有持续时间。 #1的submmatches给了我想要的东西

1     2     3     4     5     6     7             8     9     10     11
5           pm          CST         Happy Hour     for  1            hour

我希望每个可能的子匹配都填满,即使VBScript不需要它来进行正则表达式传递。我担心这就是它的工作方式,我正在努力让正则表达式为我做解析工作。我考虑通过越来越多的限制性模式运行它,直到它没有通过,然后使用最后一个传递模式,但这似乎是kludgy。

是否有可能让正则表达式填充这些子匹配?

1 个答案:

答案 0 :(得分:2)

我假设每一行都是单个单元格中的所有内容。所以我能够使用锚点。 我也认为你不需要像你一样多的捕获组。我设置了正则表达式:

Group 1        Time
Group 2        am/pm
Group 3        Time Zone
Group 4        Description
Group 5        Hours (and fractions of hours)

使用A2:An中的数据,以下例程将数据解析为相邻列。如果Submatch“未填充”并不重要。您还可以填充数组中的元素,或者您想要执行的任何其他操作。如果您想要更多子匹配,您可以随时为可选空间添加捕获组,或者将相关的非捕获组更改为捕获组。

此外,由于“for”是可选的,我选择使用前瞻来确定“描述”的结束。描述将以\ s +为\ s +序列结束;或者与“行尾”。由于我假设每个单元只有一个条目和一条线,因此多线和全局属性无关紧要。

必须在“for”之前和之后包含空格,以避免在序列中包含该序列时出现问题。

Option Explicit
'set Reference to Microsoft VBScript Regular Expressions 5.5
Sub ParseAppt()
    Dim R As Range, C As Range
    Dim RE As RegExp, MC As MatchCollection
    Dim I As Long
Set R = Range("a2", Cells(Rows.Count, "A").End(xlUp))
Set RE = New RegExp
With RE
    .Pattern = "((?:1[0-2]|0?[1-9])(?::[0-5]\d)?)\s*([ap]m)?\s*([ECMT][DS]T)?\s*(.*?(?=\s+for\s+|$))(?:\s+for\s+(\d+(?:\.\d+)?)\s*hour)?"
    .IgnoreCase = True
    For Each C In R
        If .Test(C.Text) = True Then
            Set MC = .Execute(C.Text)
            For I = 0 To 4
                C.Offset(0, I + 1) = MC(0).SubMatches(I)
            Next I
        End If
    Next C
End With
End Sub