Regex for specific pattern and group issue - regex

using regex on golang and having hard time to group three names from the following statements:
Hello Planet Earth 2022 - R1 3 Hell John v Tom v Ford
Hello World 2022 - R2 3 Hell - John v Tom v Ford
I'm trying to group "John" "Tom" "Ford" with the following regex:
^(?i).+? . R\d 3 Hell (.+?)[,|v] (.+?)[,|v] (.+?)$
The issue is that it groups "- John" for second statement and I need "John" only.
Any ideas how can it be adjusted?
thanks

Not sure how correct it is, but having tested here, I came up with this...
^(?i).+? . R\d 3 Hell[\s-]* (.+?)[,v] (.+?)[,v] (.+?)$

As you seem to match single spaces, you can optionally match a dash and a space.
Note that the last .+ does not have to be non greedy as it is the last part of the pattern, and the character class [,v] does not need a pipe char if you do not intent to match that as a character.
^(?i).+? . R\d 3 Hell (?:- )?(.+?) [,v] (.+?)[,v] (.+)
Regex demo

Related

Match first and then all equal occurrences with regex

Lets say we have the string:
one day, when Anne, Lisa and Paul went to the store, then Anne said to Paul: "I love Lisa!". Then Lisa laughed and kissed Anne.
is there a way with regex to match the first name, and then match and all other occurrences of the same name in the string?
Given the name-matching regex /[A-Z][a-z]+ (with /g maybe?), can the regex matcher be made to remember the first match, and then use that match EXACTLY for the rest of the string? Other subsequent matches to the name-matching regex should be ignored (except for Anne in the example).
The result would be (if matches are replaced with "Foo"):
one day, when Foo, Lisa and Paul went to the store, then Foo said to Paul: "I love Lisa!". Then Lisa laughed and kissed Foo.
Please ignore the fact that the sentence start uncapitalized, or add an example that also handles this.
Using a script to get the first match and then using that as input for a second iteration works of course, but that's outside the scope of the question (which is limited to ONE regex expression).
The only way I could think of is with non-fixed width lookbehinds. For example through Pypi's regex module, and maybe Javascript too? Either way, assuming a name is capture through [A-Z][a-z]+ as per your question try:
\b([A-Z][a-z]+)\b(?<=^[^A-Z]*\b\1\b.*)
See an online demo
\b([A-Z][a-z]+)\b - A 1st capture group capturing a name between two word-boundaries;
(?<=^[^A-Z]*\b\1\b.*) - A non-fixed width positive lookbehind to match start of line anchor followed by 0+ characters other than uppercase followed by the content of the 1st capture group and 0+ characters.
Here is a PyPi's example:
import regex as re
s= 'Anne, Lisa and Paul went to the store, then Anne said to Paul: "I love Lisa!". Then Lisa laughed and kissed Anne.'
s_new = re.sub(r'\b([A-Z][a-z]+)\b(?<=^[^A-Z]*\b\1\b.*)', 'Foo', s)
print(s_new)
Prints:
Foo, Lisa and Paul went to the store, then Foo said to Paul: "I love Lisa!". Then Lisa laughed and kissed Foo.

How to use REGEXTRACT to extract certain characters between two strings

I am trying to extract a person's name between different characters. For example, the cells contains this information
PATIENT: 2029985 - COLLINS, JUNIOR .
PATIENT: 1235231-02 - JERRY JR, PATRICK .
PATIENT: 986435--EXP-- - JULIUS, DANIEL .
PATIENT: 2021118-02 - DRED-HARRY, KEVIN .
My goal is to use one REGEXTRACT formula to get the following:
COLLINS, JUNIOR
JERRY JR, PATRICK
JULIUS, DANIEL
LOVE ALSTON, BRENDA
So far, I have come up with the formula:
=ARRAYFORMULA(REGEXEXTRACT(B3:B, "-(.*)\."))
Where B3 contains the first information
Using that formula, I get:
COLLINS, JUNIOR
02 - JERRY JR, PATRICK
02 - LOVE-ALSTON, BRENDA
-EXP-- - JULIUS, DANIEL
02 - DRED-HARRY, KEVIN
I managed to get the first name down but how do I go about extracting the rest.
You can use
=ARRAYFORMULA(REGEXEXTRACT(B3:B, "\s-\s+([^.]*?)\s*\."))
See the regex demo. Details:
\s-\s+ - a whitespace, -, one or more whitespaces
([^.]*?) - Group 1: zero or more chars other than a . as few as possible
\s* - zero or more whitespaces
\. - a . char.
1st solution: With your shown samples, please try following regex.
Online demo for above regex
^PATIENT:.*-\s+([^.]*?)\s*\.
OR try following Google-sheet forumla:
=ARRAYFORMULA(REGEXEXTRACT(B3:B, "^PATIENT:.*-\s+([^.]*?)\s*\."))
Explanation: Checking if line/value starts from PATIENT followed by : till -(using greedy mechanism here), which is followed by spaces(1 or more occurrences). Then creating one and only capturing group which contains everything just before .(dot) in it making it non-greedy, closing capturing group which is followed by spaces(0 or more occurrences) followed by a literal dot.
2nd solution: Using lazy match approach in regex, please try following regex.
.*?\s-\s([^.]*?)\s*\.
Google-sheet formula will be as follows:
=ARRAYFORMULA(REGEXEXTRACT(B3:B, ".*?\s-\s([^.]*?)\s*\."))
Online demo for above regex

Regex for more than 1 First Name before the Middle Initial

I'm not that good with regular expression and here is my problem:
I want to create a regex that match with a name that has two or more first name (e.g. Francis Gabriel).
I came up with the regex ^[A-Z][a-z]{3,30}/s[A-Z][a-z]{3,30} but
it only matches with two first name and not all first names.
The regex should match with John John J. Johnny.
^[A-Z][a-z]{3,30}(\\s[A-Z](\\.|[a-z]{2,30})?)*$
\s must be used in java when using a Pattern Compiler.
If it is X., we have to validate it, or XYZ
John Johny J.hny -> is wrong
so either . or [a-z] and at least one first name should be there. So, put a * at last of second part to match 0 or more.
Since java is not supported in this snippet, a JavaScript implementation of same regex is done for you to understand.
Check it here
var reg=/^[A-Z][a-z]{3,30}(\s[A-Z](\.|[a-z]{2,30})?)*$/;
console.log(reg.test("John john")); // false because second part start with small case
console.log(reg.test("John John"));
console.log(reg.test("John John J."));
console.log(reg.test("John John J. Johny"));
Use the following regex:
^\w+\s(\w+\s)+\w\.\s\w+$
^\w+\s match a name a space
(\w+\s)+ followed by at least one more name and space
\w+\.\s followed by a single letter initial with dot then space
\w+$ followed by a last name
Regex101
Test code:
String testInput = "John John P. Johnny";
if (testInput.matches("^\\w+\\s(\\w+\\s)+\\w+\\.\\s\\w+$")) {
System.out.println("We have a match");
}
Try this:
^(\S*\s+)(\S*)?\s+\S*?
Francis Gabriel - matches:
0: [0,10] Francis
1: [0,9] Francis
2: [9,9]
John John2 J. Johnny - matches:
0: [0,11] John John2
1: [0,5] John
2: [5,10] John2

Regex: Match all, but ignore a specific word

Sample 1 String:
Aquaman Figure, XL DC Comics
Sample 2 String:
Rocket Raccoon, Mini Marvel
Regex:
/(DC Comics|Marvel)/
Match Sample 1:
DC Comics
Match Sample 2:
Marvel
Works perfectly in Regex101
How do I reverse this?
I want to match Aquaman Figure, XL and Rocket Raccoon, Mini only.
Edit:
/(.+)(?=Marvel)/ seems to do the job. It excludes Marvel from Rocket Raccon! How do I make this also work with DC comics?
/(.+)(?=Marvel)/ (or /(.+)(?=DC Comics|Marvel)/ for both) isn't going to work for something like:
John Marvel Bob
For which I presume you want the result to be:
John Bob
You'll only get John in the first match, and you'll get Marvel Bob in the second match (since look-ahead doesn't consume the looked-ahead characters).
Or something that doesn't contain either of the strings (since you require that the next characters match some given characters to get a match).
The easiest solution is probably just replacing the two desired sub-strings with empty strings. Replace:
DC Comics|Marvel
with:
(empty string)
Or you can repeatedly search for:
/(.*?)(DC Comics|Marvel|$)/
And just extract the first group (which will correspond to what matches .*, which is everything starting from the end of the last match up to just before "DC Comics", "Marvel" or the end of the string).
The reluctant quantifier ? is needed to prevent the .* from matching John Marvel Bob, rather than just John in John Marvel Bob Marvel.
re.findall(r"(.*)(?=Marvel|Comics)",input)
This does exactly what you are looking for.Its in python.input will be your string.

Matching a group that may or may not exist

My regex needs to parse an address which looks like this:
BLOOKKOKATU 20 A 773 00810 HELSINKI SUOMI
-------------------- ----- -------- -----
1 2 3 4*
Groups one, two and three will always exist in an address. Group 4 may not exist. I've written a regex that helps me get the first, second and third part but I would also need the fourth part. Part 4 is the country name and can either be FINLAND or SUOMI. If the fourth part didn't exist in an address the fourth group would be empty. This is my regex so far but the third group captures the country too. Any help?
(.*?)\s(\d{5})\s(.*)$
(I'm going to be using this Oracles REGEXP function)
Change the regex to:
(.*?)\s(\d{5})\s(.+?)\s?(FINLAND|SUOMI)?$
Making group three none greedy will let you match the optional space + country choices. If group 4 doesn't match I think it will be uninitialized rather than blank, that depends on language.
To match a character (or in your case group) that may or may not exist, you need to use ? after the character/subpattern/class in question. I'm answering now because RegEx is complicated and should be explained: only posting the fix without the answer isn't enough!
A question mark matches zero or one of the preceding character, class, or subpattern. Think of this as "the preceding item is optional". For example, colou?r matches both color and colour because the "u" is optional.
Above quote from http://www.autohotkey.com/docs/misc/RegEx-QuickRef.htm
Try this:
(.*?)\s(\d{5})\s(.*?)\s?([^\s]*)?$
This will match your input more tightly and each of your groups is in its own regex group:
(\w+\s\d+\s\w\s\d+)\s(\d+)\s(\w+)\s(\w*)
or if space is OK instead of "whitespace":
(\w+ \d+ \w \d+) (\d+) (\w+) (\w*)
Group 1: BLOOKKOKATU 20 A 773
Group 2: 00810
Group 3: HELSINKI
Group 4: SUOMI (optional - doesn't have to match)
(.*?)\s(\d{5})\s(\w+)\s(\w*)
An example:
SQL> with t as
2 ( select 'BLOOKKOKATU 20 A 773 00810 HELSINKI SUOMI' text from dual
3 )
4 select text
5 , regexp_replace(text,'(.*?)\s(\d{5})\s(\w+)\s(\w*)','\1**\2**\3**\4') new_text
6 from t
7 /
TEXT
-----------------------------------------
NEW_TEXT
-----------------------------------------------------------------------------------------
BLOOKKOKATU 20 A 773 00810 HELSINKI SUOMI
BLOOKKOKATU 20 A 773**00810**HELSINKI**SUOMI
1 row selected.
Regards,
Rob.