Excel VBA regular expression error - regex

Hello I'm having some issue tring to get the regular expression code below to work.
I'm getting a application-undefine or Object-Undefine error
the text box is on user form.
the error is occurring on the line "Set allMatches = regEx.Execute(TextBox1.Text)"
not sure what I missed.
Dim regEx As Object
Dim allMatches As Object
Set regEx = CreateObject("VBScript.RegExp")
With regEx
.IgnoreCase = True
.MultiLine = False
.Pattern = "\d{2,2}/\d{2,2}/\d{4,4} \d{2,2}:\d{2,2)[AM|PM]"
.Global = True
End With
Set allMatches = regEx.Execute(TextBox1.Text)
If allMatches.Count <> 0 Then
result = allMatches.Item(0).submatches.Item(0)
End If

Ok after some googling and looking, I found the problem:
its with the pattern:
.pattern = "\d{2,2}/\d{2,2}/\d{4,4} \d{2,2}:\d{2,2**)** [AM|PM]"
it turns out you will get the 5017 error if the pattern is not valid.
by changing the ")" to the proper closing "}" the error was solved.
.pattern = "\d{2,2}/\d{2,2}/\d{4,4} \d{2,2}:\d{2,2**}** [AM|PM]"
I would thought that if the pattern did not match then you would get a false return, not so..

So it is possible that you are accessing the Textbox incorrectly. Without knowing how you setup the textbox I am going to guess that its held in the shapes collection. You could look for the textbox and then set the allMatches such as below
Dim shp As Shape
'loop through the shapes on the sheet - assuming you are working with sheet 1
For Each shp In ThisWorkbook.Sheets(1).Shapes
If shp.Name = "TextBox1" Then
Set allMatches = regEx.Execute(shp.TextFrame2.TextRange.Text)
End If
Next

Assuming you are accessing the textbox correctly, the [AM|PM] poses a problem in my mind.
It seems to me this would match one character: A, M or P, or M.
If it were me I would use [A|P]M- then the first letter could be A or P and the second letter must be M.
That would also assume it is only looking for capital letters.
To include lowercase, ([Aa]|[Pp])[Mm] OR maybe better [AaPp][Mm].

Related

Find specific instance of a match in string using RegEx

I am very new to RegEx and I can't seem to find what I looking for. I have a string such as:
[cmdSubmitToDatacenter_Click] in module [Form_frm_bk_UnsubmittedWires]
and I want to get everything within the first set of brackets as well as the second set of brackets. If there is a way that I can do this with one pattern so that I can just loop through the matches, that would be great. If not, thats fine. I just need to be able to get the different sections of text separately. So far, the following is all I have come up with, but it just returns the whole string minus the first opening bracket and the last closing bracket:
[\[-\]]
(Note: I'm using the replace function, so this might be the reverse of what you are expecting.)
In my research, I have discovered that there are different RegEx engines. I'm not sure the name of the one that I'm using, but I'm using it in MS Access.
If you're using Access, you can use the VBScript Regular Expressions Library to do this. For example:
Const SOME_TEXT = "[cmdSubmitToDatacenter_Click] in module [Form_frm_bk_UnsubmittedWires]"
Dim re
Set re = CreateObject("VBScript.RegExp")
re.Global = True
re.Pattern = "\[([^\]]+)\]"
Dim m As Object
For Each m In re.Execute(SOME_TEXT)
Debug.Print m.Submatches(0)
Next
Output:
cmdSubmitToDatacenter_Click
Form_frm_bk_UnsubmittedWires
Here is what I ended up using as it made it easier to get the individual values returned. I set a reference to the Microsoft VBScript Regular Expression 5.5 so that I could get Intellisense help.
Public Sub GetText(strInput As String)
Dim regex As RegExp
Dim colMatches As MatchCollection
Dim strModule As String
Dim strProcedure As String
Set regex = New RegExp
With regex
.Global = True
.Pattern = "\[([^\]]+)\]"
End With
Set colMatches = regex.Execute(strInput)
With colMatches
strProcedure = .Item(0).submatches.Item(0)
strModule = .Item(1).submatches.Item(0)
End With
Debug.Print "Module: " & strModule
Debug.Print "Procedure: " & strProcedure
Set regex = Nothing
End Sub

How to change case of matching letter with a VBA regex Replace?

I have a column of lists of codes like the following.
2.A.B, 1.C.D, A.21.C.D, 1.C.D.11.C.D
6.A.A.5.F.A, 2.B.C.H.1
8.ABC.B, A.B.C.D
12.E.A, 3.NO.T
A.3.B.C.x, 1.N.N.9.J.K
I want to find all instances of two single upper-case letters separated by a period, but only those that follow a number less than 6. I want to remove the period between the letters and convert the second letter to lower case. Desired output:
2.Ab, 1.Cd, A.21.C.D, 1.Cd.11.C.D
6.A.A.5.Fa, 2.Bc.H.1
8.ABC.B, A.B.C.D
12.E.A, 3.NO.T
A.3.Bc.x, 1.Nn.9.J.K
I have the following code in VBA.
Sub fixBlah()
Dim re As VBScript_RegExp_55.RegExp
Set re = New VBScript_RegExp_55.RegExp
re.Global = True
re.Pattern = "\b([1-5]\.[A-Z])\.([A-Z])\b"
For Each c In Selection.Cells
c.Value = re.Replace("$1$2")
Next c
End Sub
This removes the period, but doesn't handle the lower-case requirement. I know in other flavors of regular expressions, I can use something like
re.Replace("$1\L$2\E")
but this does not have the desired effect in VBA. I tried googling for this functionality, but I wasn't able to find anything. Is there a way to do this with a simple re.Replace() statement in VBA?
If not, how would I go about achieving this otherwise? The pattern matching is complex enough that I don't even want to think about doing this without regular expressions.
[I have a solution I worked up, posted below, but I'm hoping someone can come up with something simpler.]
Here is a workaround that uses the properties of each individual regex match to make the VBA Replace() function replace only the text from the match and nothing else.
Sub fixBlah2()
Dim re As VBScript_RegExp_55.RegExp, Matches As VBScript_RegExp_55.MatchCollection
Dim M As VBScript_RegExp_55.Match
Dim tmpChr As String, pre As String, i As Integer
Set re = New VBScript_RegExp_55.RegExp
re.Global = True
re.Pattern = "\b([1-5]\.[A-Z])\.([A-Z])\b"
For Each c In Selection.Cells
'Count of number of replacements made. This is used to adjust M.FirstIndex
' so that it still matches correct substring even after substitutions.
i = 0
Set Matches = re.Execute(c.Value)
For Each M In Matches
tmpChr = LCase(M.SubMatches.Item(1))
If M.FirstIndex > 0 Then
pre = Left(c.Value, M.FirstIndex - i)
Else
pre = ""
End If
c.Value = pre & Replace(c.Value, M.Value, M.SubMatches.Item(0) & tmpChr, _
M.FirstIndex + 1 - i, 1)
i = i + 1
Next M
Next c
End Sub
For reasons I don't quite understand, if you specify a start index in Replace(), the output starts at that index as well, so the pre variable is used to capture the first part of the string that gets clipped off by the Replace function.
So this question is old, but I do have another workaround. I use a double regex so to speak, where the first engine looks for the match as an execute, then I loop through each of those items and replace with a lowercase version. For example:
Sub fixBlah()
Dim re As VBScript_RegExp_55.RegExp
dim ToReplace as Object
Set re = New VBScript_RegExp_55.RegExp
for each c in Selection.Cells
with re `enter code here`
.Global = True
.Pattern = "\b([1-5]\.[A-Z])\.([A-Z])\b"
Set ToReplace = .execute(C.Value)
end with
'This generates a list of items that match. Now to lowercase them and replace
Dim LcaseVersion as string
Dim ItemCt as integer
for itemct = 0 to ToReplace.count - 1
LcaseVersion = lcase(ToReplace.item(itemct))
with re `enter code here`
.Global = True
.Pattern = ToReplace.item(itemct) 'This looks for that specific item and replaces it with the lowercase version
c.value = .replace(C.Value, LCaseVersion)
end with
End Sub
I hope this helps!

Regex using Interval Matching (Exact or Range)

I'm trying to use the regex feature of Ms Access 2007 VBA but I'm doing something wrong.
If I have a regex of:
\$\d{3,5}\.\d{2}
The following gives me a True: $123.45
And these give me False: $12.23 or $123456.45
So all is good with this.
However, if I now test this, it gives me True !?
$123.456
Isn't the second section of my regex, ie \d{2} stating that I can only have two digits after the decimal/period?
This problem also occurs even if I put a boundary, ie \d{2,2}
Sorry; figured it out. I used the boundaries functionality.
Therefore the following now works:
\$\d{3,5}\.\b\d{2}\b
If there are any other thoughts or suggestions I'd still be more than grateful
' you confirmed you don't want this ...
? RegExpTest("$123.456", "\$\d{3,5}\.\d{2}")
True
' you can add "$" to the regex pattern ...
? RegExpTest("$123.456", "\$\d{3,5}\.\d{2}$")
False
Function RegExpTest(ByVal pSource As String, _
ByVal pPattern As String) As Boolean
Dim re As Object ' New RegExp
Set re = CreateObject("VBScript.RegExp")
With re
.Pattern = pPattern
.IgnoreCase = True
.Global = False
.MultiLine = False
End With
RegExpTest = re.Test(pSource)
Set re = Nothing
End Function

Using RegEx to search a table for missing information doesn't extract all matching values

I am a little new to VBA, and I did try searching the forums for this topic but I am not sure I used the right words to search. Here is my question:
I am using VBA to extract missing information with regexp. Say I have a table with text which contains phone and fax numbers. I would like to collect the numbers into a table. So far, the code I have works OK, but when I have multiple numbers (say regular and 800 #s) for some reason, only one number is retrieved, not the others. How can I get all the results to be added to the table?
Query:
SELECT regxtr([Table1]![field1]) AS phone FROM Table1;
VBA code for (regxtr)function:
Option Compare Database
Function regxtr(ByVal Target As String) As String 'Target is the field we are 'extracting from
Dim re As New RegExp
Dim oMatches As Object
Dim oMatch As Object
Dim n As Long
n = 0
'Set re = CreateObject("vbscript.regexp")
With re
.Global = True
.IgnoreCase = True
.Multiline = True
.Pattern = "(\d\d\d.\d\d\d\.\d\d\d\d)" 'keeping the pattern simple for now just to test
End With
'test before executing
If re.Test(Target) = True Then
Set oMatches = re.Execute(Target)
'attempt to get all matches. THIS IS WHERE I AM FAILING
For n = 0 To oMatches.Count - 1
Set oMatch = oMatches(n)
regxtr = oMatch.Value
n = n + 1 ' does this even belong here?
Next
End If
End Function
How can I get to so all matches will populate the field [phone] in the query? Any help would be greatly appreciated.
First of all, a correction in terminology. You're not looking for 'submatches' (also called 'capturing groups' in other regex implementations). You're looking for 'matches' for your regex, so you can drop the parentheses and just use \d{3}.\d{3}.\d{4} That said, this may be what you need:
Function regxtr(ByVal Target As String) As String 'Target is the field we are 'extracting from
Dim re As New RegExp
Dim oMatches As Object
Dim oMatch As Object
With re
.Global = True
.IgnoreCase = True
.Multiline = True
.Pattern = "\d{3}.\d{3}.\d{4}" 'keeping the pattern simple for now just to test
End With
If re.Test(Target) = True Then
Set oMatches = re.Execute(Target)
For Each oMatch In oMatches 'Note: you may get multiple matches because you set re.Global = True above, otherwise you would only get the first match
regxtr = regxtr & " " & oMatch 'Note: this is awkward and not advisable as a way to return the values. This is just an example.
Next oMatch
End If
End Function
As a test:
?regxtr("foo 993-242.1231bar994.425-1234hello987.234.2424 world 999.342-5252")
993-242.1231 994.425-1234 987.234.2424 999.342-5252

using classic asp for regular expression

We have some Classic asp sites, and i'm working on them a lil' bit, and I was wondering how can I write a regular expression check, and extract the matched expression:
the expression I have is in the script's name
so Let's say this
Response.Write Request.ServerVariables("SCRIPT_NAME")
Prints out:
review_blabla.asp
review_foo.asp
review_bar.asp
How can I get the blabla, foo and bar from there?
Thanks.
Whilst Yots' answer is almost certainly correct, you can achieve the result you are looking for with a lot less code and somewhat more clearly:
'A handy function i keep lying around for RegEx matches'
Function RegExResults(strTarget, strPattern)
Set regEx = New RegExp
regEx.Pattern = strPattern
regEx.Global = true
Set RegExResults = regEx.Execute(strTarget)
Set regEx = Nothing
End Function
'Pass the original string and pattern into the function and get a collection object back'
Set arrResults = RegExResults(Request.ServerVariables("SCRIPT_NAME"), "review_(.*?)\.asp")
'In your pattern the answer is the first group, so all you need is'
For each result in arrResults
Response.Write(result.Submatches(0))
Next
Set arrResults = Nothing
Additionally, I have yet to find a better RegEx playground than Regexr, it's brilliant for trying out your regex patterns before diving into code.
You have to use the Submatches Collection from the Match Object to get your data out of the review_(.*?)\.asp Pattern
Function getScriptNamePart(scriptname)
dim RegEx : Set RegEx = New RegExp
dim result : result = ""
With RegEx
.Pattern = "review_(.*?)\.asp"
.IgnoreCase = True
.Global = True
End With
Dim Match, Submatch
dim Matches : Set Matches = RegEx.Execute(scriptname)
dim SubMatches
For Each Match in Matches
For Each Submatch in Match.SubMatches
result = Submatch
Exit For
Next
Exit For
Next
Set Matches = Nothing
Set SubMatches = Nothing
Set Match = Nothing
Set RegEx = Nothing
getScriptNamePart = result
End Function
You can do
review_(.*?)\.asp
See it here on Regexr
You will then find your result in capture group 1.
You can use RegExp object to do so.
Your code gonna be like this:
Set RegularExpressionObject = New RegExp
RegularExpressionObject.Pattern = "review_(.*)\.asp"
matches = RegularExpressionObject.Execute("review_blabla.asp")
Sorry, I can't test code below right now.
Check out usage at MSDN http://msdn.microsoft.com/en-us/library/ms974570.aspx