I need to extract a word from incoming mail's body.
I used a Regex after referring to sites but it is not giving any result nor it is throwing an error.
Example: Description: sample text
I want only the first word after the colon.
Dim reg1 As RegExp
Dim M1 As MatchCollection
Dim M As Match
Dim EAI As String
Set reg1 = New RegExp
With reg1
.Pattern = "Description\s*[:]+\s*(\w*)\s*"
.Global = False
End With
If reg1.Test(Item.Body) Then
Set M1 = reg1.Execute(Item.Body)
For Each M In M1
EAI = M.SubMatches(1)
Next
End If
Note that your pattern works well though it is better written as:
Description\s*:+\s*(\w+)
And it will match Description, then 0+ whitespaces, 1+ : symbols, again 0 or more whitespaces and then will capture into Group 1 one or more word characters (as letters, digits or _ symbols).
Now, the Capture Group 1 value is stored in M.SubMatches(0). Besides, you need not run .Test() because if there are no matches, you do not need to iterate over them. You actually want to get a single match.
Thus, just use
Set M1 = reg1.Execute(Item.body)
If M1.Count > 0 Then
EAI = M1(0).SubMatches(0)
End If
Where M1(0) is the first match and .SubMatches(0) is the text residing in the first group.
Related
I have two strings with the same amount:
Price $22.00
Price Max=$22.00
Can someone please advise how I can modify this regex pattern to make sure that the price with a "Max" in front of it will be ignored?
(?:MAX=|MAX=\s)[$]?[0-9]{0,2}?[,]?[0-9]{1,3}[.][0-9]{0,2}
You may capture the MAX= into an optional capturing group and check if it matched when all matches are found. Only grab the value if the Group 1 did not match:
Dim strPattern As String: strPattern = "(MAX=\s*)?\$\d[\d.,]*"
Dim regEx As Object
Dim ms As Object, m As Object
Set regEx = CreateObject("VBScript.RegExp")
regEx.Global = True
regEx.Pattern = strPattern
Dim t As String
t = "Price $24.00 Price Max=$22.00 "
Set ms = regEx.Execute(t)
For Each m In ms
If Len(m.SubMatches(0)) = 0 Then
Debug.Print m.value
End If
Next
The (MAX=\s*)?\$\d[\d.,]* pattern matches MAX= and 0+ whitespaces into an optional group, it matches 1 or 0 times. \$\d[\d.,]* will match a digit and any 0+ digits, commas and dots. If Len(m.SubMatches(0)) = 0 Then will check if Group 1 is not empty, and if yes, the match is valid.
One way to do it could be to match what you don't want and to capture what you do want in a capturing group using an alternation:
Max=\s*\$[0-9]+\.[0-9]+|(\$[0-9]+\.[0-9]+)
In this topic, the idea is to take "strip" the numerics, divided by a x through a RegEx. -> How to extract ad sizes from a string with excel regex
Thus from:
uni3uios3_300x250_ASDF.html
I want to achieve through RegEx:
300x250
I have managed to achieve the exact opposite and I am struggling some time to get what needs to be done.
This is what I have until now:
Public Function regExSampler(s As String) As String
Dim regEx As Object
Dim inputMatches As Object
Dim regExString As String
Set regEx = CreateObject("VBScript.RegExp")
With regEx
.Pattern = "(([0-9]+)x([0-9]+))"
.IgnoreCase = True
.Global = True
Set inputMatches = .Execute(s)
If regEx.test(s) Then
regExSampler = .Replace(s, vbNullString)
Else
regExSampler = s
End If
End With
End Function
Public Sub TestMe()
Debug.Print regExSampler("uni3uios3_300x250_ASDF.html")
Debug.Print regExSampler("uni3uios3_34300x25_ASDF.html")
Debug.Print regExSampler("uni3uios3_8x4_ASDF.html")
End Sub
If you run TestMe, you would get:
uni3uios3__ASDF.html
uni3uios3__ASDF.html
uni3uios3__ASDF.html
And this is exactly what I want to strip through RegEx.
Change the IF block to
If regEx.test(s) Then
regExSampler = InputMatches(0)
Else
regExSampler = s
End If
And your results will return
300x250
34300x25
8x4
This is because InputMatches holds the results of the RegEx execution, which holds the pattern you were matching against.
As requested by the OP, I'm posting this as an answer:
Solution:
^.*\D(?=\d+x\d+)|\D+$
Demonstration: regex101.com
Explanation:
^.*\D - Here we're matching every character from the start of the string until it reaches a non-digit (\D) character.
(?=\d+x\d+) - This is a positive lookahead. It means that the previous pattern (^.*\D) should only match if followed by the pattern described inside it (\d+x\d+). The lookahead itself doesn't capture any character, so the pattern \d+x\d+ isn't captured by the regex.
\d+x\d+ - This one should be easy to understand because it's equivalent to [0-9]+x[0-9]+. As you see, \d is a token that represents any digit character.
\D+$ - This pattern matches one or more non-digit characters until it reaches the end of the string.
Finally, both patterns are linked by an OR condition (|) so that the whole regex matches one pattern or another.
I am in need of a regex pattern, let's say i want the pattern to be any 2 or more alphabetic characters used in a group (eg: "ab"/"abb"/"abbb"/), consecutively 3 or more times in a string(eg:"ababab"/"abbabbabb"/"abbbabbbabbb"), I have this function that finds if a character is used 3 times or more consecutively in a string that i want to adapt.
Function TripleChars(S As String) As Boolean
Dim RE As Object
Set RE = CreateObject("vbscript.regexp")
With RE
.Global = True
.Pattern = "([a-z])\1\1"
.IgnoreCase = True
TripleChars = .test(S)
End With
End Function
I'm new af to regex i tried a free pattern generator and got to:
([a-z]([a-z])([a-z])?([a-z])?)\1\1\1?\1?
but i feel it's pretty unprofessional/manual having to add same statements until forever if the number gets higher...
You may use
([a-z]{2,})\1{1,100}
See the regex demo
Here,
([a-z]{2,}) - Group 1 capturing 2 or more lowercase ASCII letters
\1{1,100} - 1 to 100 consecutive occurrences of the same letter as captured in Group 1. The \1 is a numbered backreference that accesses the corresponding group value and lets you match exactly the same text (not pattern!).
Note that if you need to match exactly 7, or n occurrences, use {7} limiting quantifier.
I am just learning some regex, and I need help spitting out matches generated by my regex code. I found some very useful resources here to output anything not matched, but I want to output only the parts of a cell that do match. I am looking for dates in cells, that may be a single yyyy date or yyyy-yy, or the like (as shown from the sample data below).
Sample data:
1951/52
1909-13
2005-2014
7 . (1989)-
1 (1933/34)-2 (1935/36)
1979-2012/2013
Current Function Code: (A snippet found from an existing post here, but returns the replacement value instead of what was matched)
Function simpleCellRegex(Myrange As Range) As String
Dim regEx As New RegExp
Dim strPattern As String
Dim strInput As String
Dim strReplace As String
Dim strOutput As String
strPattern = "([12][0-9]{3}[/][0-9]{2,4})|([12][0-9]{3}[-][0-9]{2,4})|([12][0-9]{3})"
You may use
\b[12][0-9]{3}(?:[,/-][0-9]{2,4})*\b
See the regex demo
Note that \b might be removed if you are not interested in a whole word search.
Pattern details:
\b - leading word boundary (the preceding char must be either a non-word char or the start of string)
[12][0-9]{3} - 1 or 2 followed with any 3 digits
(?:[,/-][0-9]{2,4})* - zero or more sequences ((?:...)*) of:
[,/-] - a ,, / or - characters
[0-9]{2,4} - any 2 to 4 digits
\b - trailing word boundary (there must be a non-word char or the end of string after).
Sample VBA code to grab all those values using RegExp#Execute:
Sub FetchDateLikeStrs()
Dim cellContents As String
Dim reg As regexp
Dim mc As MatchCollection
Dim m As match
Set reg = New regexp
reg.pattern = "\b[12][0-9]{3}(?:[,/-][0-9]{2,4})*\b"
reg.Global = True
cellContents = "1951/52 1909-13 2005-2014 7 . (1989)- 1 (1933/34)-2 (1935/36) 1979-2012/2013 1951,52"
Set mc = reg.Execute(cellContents)
For Each m In mc
Debug.Print m.Value
Next
End Sub
Regex in VBA.
I am using the following regex to match the second occurance of a 4-digit group, or the first group if there is only one group:
\b\d{4}\b(?!.+\b\d{4}\b)
Now I need to do kind of the opposite: I need to match everything up until the second occurance of a 4-digit group, or up until the first group if there is only one. If there are no 4-digit groups, capture the entire string.
This would be sufficient.
But there is also a preferable "bonus" route: If there exists a way to match everything up until a 4-digit group that is optionally followed by some random text, but only if there is no other 4-digit group following it. If there exists a second group of 4 digits, capture everything up until that group (including the first group and periods, but not commas). If there are no groups, capture everything. If the line starts with a 4-digit group, capture nothing.
I understand that also this could (should?) be done with a lookahead, but I am not having any luck in figuring out how they work for this purpose.
Examples:
Input: String.String String 4444
Capture: String.String String 4444
Input: String4444 8888 String
Capture: String4444
Input: String String 444 . B, 8888
Capture: String String 444 . B
Bonus case:
Input: 8888 String
Capture:
for up until the second occurrence of a 4-digit group, or up until the first group if there is only one use this pattern
^((?:.*?\d{4})?.*?)(?=\s*\b\d{4}\b)
Demo
per comment below, use this pattern
^((?:.*?\d{4})?.*?(?=\s*\b\d{4}\b)|.*)
Demo
You can use this regex in VBA to capture lines with 4-digit numbers, or those that do not have 4-digit numbers in them:
^((?:.*?[0-9]{4})?.*?(?=\s*?[0-9]{4})|(?!.*[0-9]{4}).*)
See demo, it should work the same in VBA.
The regex consists of 2 alternatives: (?:.*?[0-9]{4})?.*?(?=\s*?[0-9]{4}) and (?!.*[0-9]{4}).*.
(?:.*?[0-9]{4})?.*?(?=\s*?[0-9]{4}) matches 0 or more (as few as possible) characters that are preceded by 0 or 1 sequence of characters followed by a 4-digit number, and are followed by optional space(s) and 4 digit number.
(?!.*[0-9]{4}).* matches any number of any characters that do not have a 4-digit number inside.
Note that to only match whole numbers (not part of other words) you need to add \b around the [0-9]{4} patterns (i.e. \b[0-9]{4}\b).
Matches everything except spaces till last occurace of a 4 digit word
You can use the following:
(?:(?! ).)+(?=.*\b\d{4}\b)
See DEMO
For your basic case (marked by you as sufficient), this will work:
((?:(?!\d{4}).)*(?:\d{4})?(?:(?!\d{4}).)*)(?=\d{4})
You can pad every \d{4} internally with \b if you need to.
See a demo here.
If anyone is interested, I cheated to fully solve my problem.
Building on this answer, which solves the vast majority of my data set, I used program logic to catch some rarely seen use-cases. It seemed difficult to get a single regex to cover all the situations, so this seems like a viable alternative.
Problem is illustrated here.
The code isn't bulletproof yet, but this is the gist:
Function cRegEx (str As String) As String
Dim rExp As Object, rMatch As Object, regP As String, strL() As String
regP = "^((?:.*?[0-9]{4})?.*?(?:(?=\s*[0-9]{4})|(?:(?!\d{4}).)*)|(?!.*[0-9]{4}).*)"
' Encountered two use-cases that weren't easily solvable with regex, due to the already complex pattern(s).
' Split str if we encounter a comma and only keep the first part - this way we don't have to solve this case in the regex.
If InStr(str, ",") <> 0 Then
strL = Split(str, ",")
str = strL(0)
End If
' If str starts with a 4-digit group, return an empty string.
If cRegExNum(str) = False Then
Set rExp = CreateObject("vbscript.regexp")
With rExp
.Global = False
.MultiLine = False
.IgnoreCase = True
.Pattern = regP
End With
Set rMatch = rExp.Execute(str)
If rMatch.Count > 0 Then
cRegEx = rMatch(0)
Else
cRegEx = ""
End If
Else
cRegEx = ""
End If
End Function
Function cRegExNum (str As String) As Boolean
' Does the string start with 4 non-whitespaced integers?
' Return true if it does
Dim rExp As Object, rMatch As Object, regP As String
regP = "^\d{4}"
Set rExp = CreateObject("vbscript.regexp")
With rExp
.Global = False
.MultiLine = False
.IgnoreCase = True
.Pattern = regP
End With
Set rMatch = rExp.Execute(str)
If rMatch.Count > 0 Then
cRegExNum = True
Else
cRegExNum = False
End If
End Function