我正在尝试复制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。
是否有可能让正则表达式填充这些子匹配?
答案 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