Regex - Stop after finding the first pattern - python-3.x

For a string like this:
1. Jane, Doe2. Good, Jay3. Turn, Bob[key]
Either Jane, Doe needs to be extracted if no [key] is present then whatever is between 1. and 2.
(or)
Turn, Bob if [key] is present
Put another way:
If [key] is present, then the person before [key] needs to be extracted and the process stopped.
If [key] is not present, then pick up whoever is after 1.
I tried this but it pulls up both Jane, Doe and Turn, Bob
(\.([^\.])(.+)\[key\])|(1\.(.+)2\.)
How to stop after finding the first successful pattern, knowing that patterns are read left to right? [key] can be anyone - 1,2 or 3.
Thanks.

For these requirements, you may use this regex in Python with an alternation:
(?<=\d\.\s)[a-zA-Z, ]+(?=\[key])|(?<=1\.\s)(?!.*\[key])[a-zA-Z, ]+
RegEx Demo
RegEx Details:
(?<=\d\.\s): Positive lookbehind to assert that there is a digit followed by dot followed by a whitespace before the current position
[a-zA-Z, ]+: Match 1+ of letter, space or comma characters
(?=\[key]): Positive lookahead to assert that there is a text [key] after the current position
|: OR
(?<=1\.\s): Positive lookbehind to assert that there is a digit 1 followed by dot followed by a whitespace before the current position
(?!.*\[key]): Negative lookbehind to assert that there is no [key] text after the current position
[a-zA-Z, ]+: Match 1+ of letter, space or comma characters

Not sure why you put .+ into your regex but it's greedy and matches . Good, Jay3. Turn, Bob. so the left part of the alternation matches.
Suggest you remove the .+ on both sides of the alternation ( | ).

Related

Append characters based on the count of a match in Vim

I would like to append - at the end of each word match. But, the number of - appended should be based on the count of the match, so that the total number of characters in that line remain constant.
As shown in the example below, the total number of characters should be 6.
e.g.
ab
xyz
abcde
The above text should be replaced to:
ab----
xyz---
abcde-
You can use \= to substitute with an expression, see :h sub-replace-expression.
When the substitute string starts with \=, the remainder is interpreted as an expression.
The submatch() function can be used to obtain matched text. The whole matched text can be accessed with submatch(0). The text matched with the first pair of () with submatch(1). Likewise for further sub-matches in ().
So you can achieve it like this:
:[range]s//\=submatch(0) . repeat('-', 6-strlen(submatch(0)))/

Looking for a Regex which can find all the number combinaitions without having 3 zero's in between and mixed with delimeters

I would like to find all the number combinaitions without having 3 zero's in between.
There might be some delimiters (max 2 characters) in between the numbers.
I'm using python and I would like to perform this search with the regex.
Accepted numbers
This is number 1234 which should be accepted.
12-45
1 2 0 0 3 4 5
not accepted numbers:
1
12
123
1000
1000-2000
30000-31000
21 000-32 000-50 000
21 00 03 00 00
The regex with which I could come up is:
([\s\-]{0,2}\d(?!000)){4,}
My regex can find all the accepted numbers but it doesn't filter out all the excepted numbers.
See the results in regex
Actually this regex is used in python to remove the matched numbers from the text:
See python code
p.s. Delimiters are not only space but should be at least \s and dash.
p.s.s. The numbers might be in the middle of the string. So I think I cannot use ^ and $ in my regex.
You could assert not 3 zeroes in a row while matching optional delimiters in between.
\b(?![\d\s-]*?0(?:[\s-]*0){2})\d(?:[\s-]*\d){3,}\b
Explanation
\b A word boundary
(?! Negative lookahead, assert what is at the right is not
[\d\s-]*? Match any of a digit, whitespace char or - as least as possible
0(?:[\s-]*0){2} - ) Match a zere followed by 2 times a zero with optional delimiters in between
\d Match a digit
(?:[\s-]*\d){3,} Repeat 3 or more times matching a digit with optional delimiters in between
\b A word boundary
Regex demo

Regex to match between 2 and 5 characters, one of which must be alphabetic

I'm not great with regex and the following has me stumped.
I need to find all the matches in a string that are between 2 and 5 characters [A-Z0-9] only, and must contain at least one alphabetic character [A-Z]
So
A1 - Match
AAA - Match
AAAAAA - No Match
A1234 - Match
123 - No Match
A123A - Match
A - No Match
1 - No Match
A1B2C3 - No Match
I have tried this:
([A-Z0-9]*[A-Z][A-Z0-9]*){2,5}
But it doesnt limit the total length of the match to between 2 and 5 characters
You can use
\b(?=\d*[A-Z])[A-Z\d]{2,5}\b
\b(?=[A-Z0-9]{2,5}\b)[A-Z0-9]*[A-Z][A-Z0-9]*\b
See the regex demo #1 and the regex demo #2. Details:
\b - word boundary
(?=\d*[A-Z]) - after zero or more digits, there must be an uppercase ASCII letter
(?=[A-Z0-9]{2,5}\b) - there must be 2 to 5 alnum chars up to the word boundary
[A-Z0-9]* - zero or more uppercase ASCII letters or digits
[A-Z] - an uppercase ASCII letter
[A-Z\d]{2,5} - two to five uppercase ASCII letters or digits
\b - word boundary.
See the Python demo:
import re
text = "A1 AAA....A1234!!!!~A123A abc,AAAAAA,123,A,1,A1B2C3"
print(re.findall(r'\b(?=[A-Z0-9]{2,5}\b)[A-Z0-9]*[A-Z][A-Z0-9]*\b', text))
# => ['A1', 'AAA', 'A1234', 'A123A']
Try this one
^([A-Z][A-Z0-9]{1,4}|[A-Z0-9][A-Z][A-Z0-9]{,3}|[A-Z0-9]{1,2}[A-Z][A-Z0-9]{,3}|[A-Z0-9][A-Z][A-Z0-9])$

How do I remove text using sed?

For instance let say I have a text file:
worker1, 0001, company1
worker2, 0002, company2
worker3, 0003, company3
How would I use sed to take the first 2 characters of the first column so "wo" and remove the rest of the text and attach it to the second column so the output would look like this:
wo0001,company1
wo0002,company2
wo0003,company3
$ sed -E 's/^(..)[^,]*, ([^,]*,) /\1\2/' file
wo0001,company1
wo0002,company2
wo0003,company3
s/ begin substitution
^(..) match the first two characters at the beginning of the line, captured in a group
[^,]* match any amount of non-comma characters of the first column
, match a comma and a space character
([^,]*,) match the second field and comma captured in a group (any amount of non-comma characters followed by a comma)
match the next space character
/\1\2/ replace with the first and second capturing group

Matching only a <tab> that is between two numbers

How to match a tab only when it is between two numbers?
Sample script
209.65834 27.23204908
119.37987 15.03317082
74.240635 8.30561924
29.1014 0
931.8861 -100.00000
-16.03784 -8.30562
;
_mirror
l
;
29.1014 0
1028.10 0.00
n
_spline
935.4875 250
924.2026913 269.8820375
912.9178825 277.4506484
890.348265 287.3181854
(in the above script, the tabs are between the numbers, not the spaces) (blank lines are significant; there is nothing in them, but I can't lose them)
I wish to get a "," between the numbers. Tried with :%s/\t/\,/ but that will touch the empty lines too, and the end of lines.
Try this:
:%s/\(\d\)\t\(-\?\d\)/\1,\2/
\d matches any digit. -? means "an optional -. The pair of (escaped) parenthesis capture the match, and \1 refers to the first captured match, \2 refers to the second.
google://vim+regex -> http://vimregex.com/ ->
:%s/\([0-9]\)\t\([0-9]\)/\1,\2/gc
You have 2 groups of numbers here ([0-9]) and tab-symbols \t between them. Add some escape symbols and you have the answer.
g for multichange in single line, c for some asking.
\1 and \2 are matching groups (numbers in your case).
It's not really hard to find answer for questions like that by yourself.
try
:%s/\([0-9]\)\t\([0-9]\)/\1,\2/g
explanation - search the patten <digit>\t<digit> and remember the part that matches <digit> .
\( ... \) captures and remembers the part that matches.
\1 recalls the first captured digit, \2 the second captured digit.
so if the match was on 123\t789, <digit>,<digit> matches 3\t7
the 3 and 7 are rememberd as \1 and \2
or
:g/[0-9]/ s/\t/,/g
explanation - filter all lines with a digit, then substitute tabs with a comma on those lines

Resources