Regex to match string with optional section preceded by space - regex

I need help figuring out how to match a string that will have optional sections. The issue is that the optional section will be prefixed by a space and < and suffixed by >.
This is what I have currently
^([\\w,:\\s.]+)\\s-\\s<([A-Z]+)>\\s<([\\s\\w-]+)>\\s<([\\w-]+)>(\\s<[\\d.]+>)?(\\s<[\\d]+>)?\\s<([\\w.]+)>\\s-\\s<(.+?(?=>$|$))
This is the string I am trying to match
Jul 09, 2022 03:05:12.570 AM - <DEBUG> <Default Executor-thread-26> <logging-poc> <100.99.88.1> <123456> <myco> - <Inside getDebugLog()>
The section (\\s<[\\d.]+>)? and (\\s<[\\d]+>)? correspond to the ip address and account. As it is currently, the match ends up including the space and < >.
Record map{logdatetime=Jul 09, 2022 03:05:12.570 AM, severity=DEBUG, thread=Default Executor-thread-26, application=logging-poc, ip= <100.99.88.1>, account= <123456>, module=myco, message=Inside getDebugLog()>}
I only want the value for the ip and the account like below
Record map{logdatetime=Jul 09, 2022 03:05:12.570 AM, severity=DEBUG, thread=Default Executor-thread-26, application=logging-poc, ip=100.99.88.1, account=123456, module=myco, message=Inside getDebugLog()>}
The pattern should also work for this line of log (where the optional sections have been removed)
Jul 09, 2022 03:05:12.570 AM - <DEBUG> <Default Executor-thread-26> <logging-poc> <myco> - <Inside getDebugLog()>
Thank you.

You have an issue in your 5th and 6th capturing groups.
5th group: (\s<[\d.]+>)?
6th group: (\s<[\d]+>)?
These will capture the surrounding spaces and </>. The solution is to capture the information you desire in a subgroup:
5th (and now 6th) group: (\s<([\d.]+)>)?
The now-7th (and 8th) group: (\s<([\d]+)>)?
The old group 7 is now group 9, and the old group 8 is now group 10.
Per your comments below, since you need to keep the same group indices, you can make use of a non-capturing group, using the syntax (?:pattern).
5th group (inner parenthesis): (?:\s<([\d.]+)>)?
6th group (inner parenthesis): (?:\s<([\d]+)>)?
The end-result regex (using Java escapes):
^([\\w,:\\s.]+)\\s-\\s<([A-Z]+)>\\s<([\\s\\w-]+)>\\s<([\\w-]+)>\\s(?:<([\\d.]+)>)?(?:\\s<([\\d]+)>)?\\s<([\\w.]+)>\\s-\\s<(.+?(?=>$|$))
This will not change the indices of the groups you have captured, and will allow you to capture only the parts that you are interested in, while still being able to denote the outer group as optional (via ()?).

Related

Need improvement on the Regex

I have wrote an easy regex for extracting user SC08.
https://regex101.com/r/L1DOzH/1/ Performance wise, its really bad taking around 1448 steps.
Jun 2 11:16:44 192.168.55.19 1 2020-06-02T10:16:43.721Z chisdsm#abcd.com dsm 4493 USR1278I [U#21513 sev="INFO" msg="user logged out due to inactivity" user="SC08"]
Jun 2 10:13:50 192.168.55.19 1 2020-06-02T09:13:50.297Z chisdsm#abcd.com dsm 4493 DO0426I [DA#21513 sev="INFO" msg="switch domain" admin="SC08"
Jun 2 10:13:43 192.168.55.19 1 2020-06-02T09:13:42.956Z chisdsm#abcd.com dsm 4493 DAO0267I [DA#21513 sev="INFO" msg="user logged in" admin="SC08" stime="2020-06-02 10:13:42.944" role="ALL_ADMIN" source="192.168.54.9"]
May 27 15:53:38 192.168.55.129 1 2020-05-27T14:53:37.669Z chisdsm#abcd.com dsm 4493 DAO0227I [DA#21513 sev="INFO" msg="delete file signature" user="SC08" filePath="/bin/rm"]
Alternation group as the first pattern in a regex cancels some optimizations that are in place for patterns that start with a more specific pattern.
Since your alternatives match = delimited strings, you may put it at the beginning of the pattern, and then use lookarounds, as in Michail's suggestion. Here is a small variation with 139 steps:
=(?:(?<=user=)"(?<user1>\w+)|(?<=admin=)"(?<user2>\w+))
See the regex demo. Details
= - an equals sign
(?:(?<=user=)"(?<user1>\w+)|(?<=admin=)"(?<user2>\w+)) - a non-capturing group:
(?<=user=) - user= must be immediately to the left of the current position
" - a " char
(?<user1>\w+) - Group "user1": 1+ word chars
| - or
(?<=admin=) - admin= must be immediately to the left of the current position
" - a " char
(?<user2>\w+) - Group "user2": 1+ word chars
If your matches are always preceded with a whitespace, use it as the first pattern:
\s(?:user="(?<user1>\w+)|admin="(?<user2>\w+))
See this regex demo, with 918 steps.
If you know the matches are somewhere close to the end of the line, use
.*\b(?:user="(?<user1>\w+)|admin="(?<user2>\w+))
See this regex demo, 568 steps. .* at the start will move the regex index at the end of a line/string and then backtrack to find either user= or admin=.

Matching possible date elements in range

I'm having difficulty matching other cases for a date range. The end goal will be to extract each group to build an ISO 8601 date format.
Test cases
May 8th – 14th, 2019
November 25th – December 2nd
November 5th, 2018 – January 13th, 2019
September 17th – 23rd
Regex
(\w{3,9})\s([1-9]|[12]\d|3[01])(?:st|nd|rd|th),\s(19|20)\d{2}\s–\s(\w{3,9})\s([1-9]|[12]\d|3[01])(?:st|nd|rd|th),\s(19|20)\d{2}
regexr
I would like to be able to capture each group regardless if it exists or not.
For example, May 8th – 14th, 2019
Group 1 May
Group 2 8th
Group 3
Group 4
Group 5 14th
Group 6 2019
And November 5th, 2018 – January 13th, 2019
Group 1 November
Group 2 5th
Group 3 2018
Group 4 January
Group 5 13th
Group 6 2019
To capture the empty string if the group doesn't match otherwise, the general idea is to use (<characters to match>|)
Try this one:
([A-z]{3,9})\s((?:[1-9]|[12]\d|3[01])(?:st|nd|rd|th))(?:, (?=19|20))?(\d{4}|)\s–\s([A-z]{3,9}|)\s?((?:[1-9]|[12]\d|3[01])(?:st|nd|rd|th))(?:, (?=19|20))?(\d{4}|)
https://regex101.com/r/4UY0WE/1/
When trying to capture the month (the first group), make sure to use [A-z]{3,9} rather than \w{3,9}, otherwise you might match, eg, 23rd rather than a month string.
Separated out:
([A-z]{3,9}) # Month ("January")
\s
((?:[1-9]|[12]\d|3[01])(?:st|nd|rd|th)) # Day of month, including suffix ("23rd")
(?:, (?=19|20))? # Comma and space, if followed by year
(\d{4}|) # Year
\s–\s #
([A-z]{3,9}|) # same as first line
\s?
# same as third to fifth lines:
((?:[1-9]|[12]\d|3[01])(?:st|nd|rd|th))
(?:, (?=19|20))?
(\d{4}|)
This one saves some space by consolidating some of the groupings.
Try it here
Full regex:
([A-z]{3,9}) ((?:[1-9]|[12]\d|3[01])(?:st|nd|rd|th))(?:, ((?:19|20)\d{2}))? [–-] ([A-z]{3,9}\s)?((?:[1-9]|[12]\d|3[01])(?:st|nd|rd|th))(?:, ((?:19|20)\d{2}))?
Separated by group (spaces replaced by \s for readability):
1. ([A-z]{3,9})
\s
2. ((?:[1-9]|[12]\d|3[01])(?:st|nd|rd|th))
3. (?:,\s((?:19|20)\d{2}))?
\s[–-]\s
4. ([A-z]{3,9}\s)?
5. ((?:[1-9]|[12]\d|3[01])(?:st|nd|rd|th))
6. (?:,\s((?:19|20)\d{2}))?
This method does not use lookups so is generally safe for any regex engine.

How to creating a regex pattern in VBA to extract dates from string and exclude false matches

I am trying to use Regex to parse a series of strings to extract one or more text dates that may be in multiple formats. The strings will look something like the following:
24 Aug 2016: nno-emvirt010a/b; 16 Aug 2016 nnt-emvirt010a/b nnd-emvirt010a/b COSI-1.6.5
24.16 nno-emvirt010a/b nnt-emvirt010a/b nnd-emvirt010a/b EI.01.02.03\
9/23/16: COSI-1.6.5 Logs updated at /vobs/COTS/1.6.5/files/Status_2016-07-27.log, Status_2016-07-28.log, Status_2016-08-05.log, Status_2016-08-08.log
I am not concerned about validating the individual date fields; just extracting the date string. The part I am unable to figure out is how to not match on number sequences that match the pattern but aren’t dates (‘1.6.5’ in ex. (1) and 01.02.03 in ex. (2)) and dates that are part of a file name (2016-07-27 in ex. (3)). In each of these exception cases in my input data, the initial numbers are preceded by either a period(.), underscore (_) or dash (-), but I cannot determine how to use this to edit the pattern syntax to not match these strings.
The pattern I have that partially works is below. It will only ignore the non date matches if it starts with 1 digit as in example 1.
/[^_\.\(\/]\d{1,4}[/\-\.\s*]([1-9]|0[1-9]|[12][0-9]|3[01]|[a-z]{3})[/\-\.\s*]\d{1,4}/ig`
I am not sure about vba check if this works . seems they have given so much options : https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9781449327453/ch04s04.html
^(?:(1[0-2]|0?[1-9])/(3[01]|[12][0-9]|0?[1-9])|↵
(3[01]|[12][0-9]|0?[1-9])/(1[0-2]|0?[1-9]))/(?:[0-9]{2})?[0-9]{2}$
^(?:
# m/d or mm/dd
(1[0-2]|0?[1-9])/(3[01]|[12][0-9]|0?[1-9])
|
# d/m or dd/mm
(3[01]|[12][0-9]|0?[1-9])/(1[0-2]|0?[1-9])
)
# /yy or /yyyy
/(?:[0-9]{2})?[0-9]{2}$
According to the test strings you've presented, you can use the following regex
See this regex in use here
(?<=[^a-zA-Z\d.]|^)((?:\d{1,2}\s*[A-Z][a-z]{2}\s*\d+)|(?:(?:\d{1,2}\/){2}\d+)|(?:\d+(?:-\d{2}){2})|\d{2}\.\d{2})(?=[^a-zA-Z\d.])
This regex ensures that specific date formats are met and are preceded by nothing (beginning of the string) or by a non-word character (specifically a-z, A-Z, 0-9) or dot .. The date formats that will be matched are:
24 Aug 2016
24.16
9/23/16
The regex could be further manipulated to ensure numbers are in the proper range according to days/month, etc., however, I don't feel that is really necessary.
Edits
Edit 1
Since VBA doesn't support lookbehinds, you can use the following. The date is in capture group 1.
(?:[^a-zA-Z\d.]|^)((?:\d{1,2}\s*[A-Z][a-z]{2}\s*\d+)|(?:(?:\d{1,2}\/){2}\d+)|(?:\d+(?:-\d{2}){2})|\d{2}\.\d{2})(?=[^a-zA-Z\d.])
Edit 2
As per bulbus's comment below
(?:[^\w.]|^)((?:\d{1,2}\s*[A-Z][a-z]{2}\s*\d{2,4})|(?:(?:\d{‌1,2}\/){2}\d{2,4})|(‌​?:\d{2,4}(?:-\d{2}){‌​2})|\d{2}\.\d{2})
Took liberty to edit that a bit.
replaced [^a-zA-Z\d.] with [^\w.], comes with added advantage of excluding dates with _2016-07-28.log
Due to 1 removed trailing condition (?=[^a-zA-Z\d.]).
Forced year digits from \d+ to \d{2,4}
Edit 3
Due to added conditions of the regex, I've made the following edits (to improve upon both previous edits). As per the OP:
The edited pattern above works in all but 2 cases:
it does not find dates with the year first (ex. 2016/07/11)
if the date is contained within parenthesis in the string, it returns the left parenthesis as part of the date (ex. match = (8/20/2016)
Can you provide the edit to fix these?
In the below regexes, I've changed years to \d+ in order for it to work on any year greater than or equal to 0.
See the code in use here
(?:[^\w.]|^)((?:\d{1,2}\s+[A-Z][a-z]{2}\s+\d+)|(?:(?:\d{1,2}\/){2}\d+)|(?:\d+(?:\/\d{1,2}){2})|(?:\d+(?:-\d{2}){2})|\d{2}\.\d+)
This regex adds the possibility of dates in the XXXX/XX/XX format where the date may appear first.
The reason you are getting ( as a match before the regex is the nature of the Full Match. You need to, instead, grab the value of the first capture group and not the whole regex result. See this answer on how to grab submatches from a regex pattern in VBA.
Also, note that any additional date formats you need to catch need to be explicitly set in the regex. Currently, the regex supports the following date formats:
\d{1,2}\s+[A-Z][a-z]{2}\s+\d+
12 Apr 17
12 Apr 2017
(?:\d{1,2}\/){2}\d+
1/4/17
01/04/17
1/4/2017
01/04/2017
\d+(?:\/\d{1,2}){2}
17/04/01
2017/4/1
2017/04/01
17/4/1
\d+(?:-\d{2}){2}
17-04-01
2017-04-01
\d{2}\.\d+ - Although I'm not sure what this date format is even used for and how it could be considered efficient if it's missing month
24.16

Regex for capturing different date formats

I'm tasked to capture date for itineraries in email message, but the dates given were all in different formats, I guess I need help to find out if there's any way to capture the following formats:
02 APR
APR 02
2 APR
APR 2
2nd APR
APR 2nd
2nd April
April 2nd
APR 12th
April 12th
12th April
April 13-16
13-16 April
APR 13-16
13-16 APR
April 13th-16th
13th-16th April
APR 13th-16th
13th-16th APR
I've tried numerous ways but just could not understand or fathom as I'm a
newbie to regex.
The closest I could get was using this:
(\d*)-(\d*) APR|April \d*\d*
EDIT- Found out that i`ve missed some more formats.
13th - 16th APR
13~16 April
13/16 APR
I`ve tried using the following:
(Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\ *\d+(?:[nr]d|th|st)?(?: * \d+(?:[nr]d|th|st)?)?|\d+(?:[nr]d|th|st)?(?: . \d+(?:[nr]d|th|st)?)?\ *(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)
Could either capture dates with space or without space.
Is there a way to capture all formats, and split the dates with '-', '/','~' and output/write into a single standardize format?
(Group 1 Date)-Month (Group 2 Date)-Month eg: 13-Apr 16-Apr
Appreciate for your kind suggestions and comments.
You need to account for optional values. Here is an enhanced version matching your sample input:
/(\d+)(?:st|[nr]d|th)?-?(\d*)(?:st|[nr]d|th)?\s*Apr(?:il)?|Apr(?:il)?\s*(\d+)(?:st|[nr]d|th)?-?(\d*)(?:st|[nr]d|th)?/i
See the regex demo (note you need to use a case-insensitive modifier to match any variants of April)
Basically, there are 2 alternatives matching April and date ranges:
(\d+)(?:st|[nr]d|th)?-?(\d*)(?:st|[nr]d|th)?\s*Apr(?:il)? - 1+ digits followed with an optional st, nd, rd, th, followed with an optional hyphen, followed with 0+ digits, followed with optional st, etc. followed with 0+ whitespace and then Apr or April (case insensitive due to /i modifier)
| - or
Apr(?:il)?\s*(\d+)(?:st|[nr]d|th)?-?(\d*)(?:st|[nr]d|th)? - the same as above but swapped.
I came up with this Regex:
(?:APR|April)\ *\d+(?:[nr]d|th|st)?(?:-\d+(?:[nr]d|th|st)?)?|\d+(?:[nr]d|th|st)?(?:-\d+(?:[nr]d|th|st)?)?\ *(?:APR|April)
See details here: Regex101
Maybe it's overkill, but I came up with this regex that will match with any month:
(?:January|JAN|February|FEB|March|MAR|April|APR|May|MAY|June|JUN|July|JUL|August|AUG|September|SEP|October|OCT|November|NOV|December|DEC)\ *\d+(?:[nr]d|th|st)?(?:-\d+(?:[nr]d|th|st)?)?|\d+(?:[nr]d|th|st)?(?:-\d+(?:[nr]d|th|st)?)?\ *(?:January|JAN|February|FEB|March|MAR|April|APR|May|MAY|June|JUN|July|JUL|August|AUG|September|SEP|October|OCT|November|NOV|December|DEC)
Unreadable, check here if you want details: Regex101
Improved version using Wiktor Stribiżew's trick:
(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\ *\d+(?:[nr]d|th|st)?(?:-\d+(?:[nr]d|th|st)?)?|\d+(?:[nr]d|th|st)?(?:-\d+(?:[nr]d|th|st)?)?\ *(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)
See details here: Regex101
It matches every monthes, it uses less steps (more efficient)
BUT, you need to make sure you're case insensitive
I came up with this:
(\d+(?:th|st|[nr]d)?(?:-\d+(?:th|st|[nr]d)?)?\s*(?:APR|April))|((?:APR|April)\s*\d+(?:th|st|[nr]d)?(?:-\d+(?:th|st|[nr]d)?)?)
Live Demo

Regex expression matches without specifying comma (,) in regex

I am taking help of this website to learn regex and I am stuck at this particular lesson. Looks like regex is wrong there.
When I write (\w+\s\d+)((\,\d+)?) "text" and "capture" goes green but "result" appears wrong (cross marks).
But if Write (\w+ (\d+)) it gives below result.
your task text capture result
capture text Jan 1987 Jan 1987, 1987 ✓
capture text May 1969 May 1969, 1969 ✓
capture text Aug 2011 Aug 2011, 2011 ✓
Now, question is (\w+ (\d+)) doesn't show that it going to capture comma but is right answer.And, in this (\w+\s\d+)((\,\d+)?) expression I have specified but it is coming wrong, why?
That's because the capture column tells you, what you should capture. For example: Jan 1987, 1987 means you should capture two groups. 1) Jan 1987 2) 1987
They use the comma as divider between the groups. So it's not part of the string you should capture, but just a divider to tell you where the next excepted capture group starts.
If you step to the next lesson http://regexone.com/lesson/13 my example will be much more clear. In the text column there isn't any comma (e.g. 1280x720) but in capture column you're asked for "1280, 720". So this props my theory.