Extract array variable using regex with particular text within - regex

This is my search string:
abc[0] = xyz[(3/*my-const:VECT_E->_numget_C*/)];
and I wrote this regex pattern:
(?<=)(\w*)(?=\[)
but this is matching to "abc" and I want to extract the array variable which has the commented code within "/*my-const....". So the output expect as xyz than abc.
Please check if I made any mistake in the regex.

You can use
(?<=\s)\w+(?=\[)
(?!^)\b\w+(?=\[)
See regex #1 and regex #2 demo.
Details
(?<=\s) - a positive lookbehind that requires a whitespace to appear immediately to the left of the current location
(?!^)\b - a location not at the start of string, but at the word boundary (the next char is a word char, so the char right before cannot be a word char)
\w+ - one or more word chars
(?=\[) - a positive lookahead that requires a [ char to appear immediately to the right of the current location.

Related

Regexp regular/recursive find/replace in Notepad++

How to split some strings defined in a specific format:
[length namevalue field]name=value[length namevalue field]name=value[length namevalue field]name=value[length namevalue field]name=value
Is it possible with a Find/Replace regex in Notepad++ isolate the pair name=value replacing [length namevalue field] with a white space?
The main problem is related to numeric value where a simple \d{4} search doesn't work.
Eg.
INPUT:
0010name=mario0013surname=rossi0006age=180006phone=0014address=street
0013name=marianna0013surname=rossi0006age=210006phone=0015address=street1
0003name=pia0015surname=rossini0005age=30017phone=+39221122330020address=streetstreet
OUTPUT:
name=mario surname=rossi age=18 phone= address=street
name=mario surname=rossi age=18 phone= address=street
name=marianna surname=rossi age=21 phone= address=street1
name=pia surname=rossini age=3 phone=+3922112233 address=streetstreet
You can use
\d{4}(?=[[:alpha:]]\w*=)
\d{4}(?=[^\W\d]\w*=)
See the regex demo.
The patterns match
\d{4} - four digits
(?=[[:alpha:]]\w*=) - that are immediately followed with a letter and then any zero or more word chars followed with a = char immediately to the right of the current position.
(?=[^\W\d]\w*=) - that are immediately followed with a letter or an underscore and then any zero or more word chars followed with a = char immediately to the right of the current position.
In Notepad++, if you want to remove the match at the start of the line and replace with space anywhere else, you can use
^(\d{4}(?=[[:alpha:]]\w*=))|(?1)
and replace with (?1: ). The above explained pattern, \d{4}(?=[[:alpha:]]\w*=), is matched and captured into Group 1 if it is at the start of a line (^), and just matched anywhere else ((?1) recurses the Group 1 pattern, so as not to repeat it). The (?1: ) replacement means we replace with empty string if Group 1 matched, else, we replace with a space.
See the demo screenshot:

Regex To Match String With All Words Contains Certain Format

I want to validate a field of string so that it only accept string that contains words with certain format.
Example accepted string:
#key;
#key1; #key2;#key3;
Example rejected string:
key;
%key1X #key2X$key3X
My regex:
\B(\#[a-zA-Z0-9_; ]+\b)(\;)
It seems my regex still accept a string as long as it has a word with valid format, while I only want it to be accepted if whole words are in the correct format.
Current example:
%key1; %key2 #keysz;#key3; #key4;
From the above Current Example still accepted because it contains #keysz; and #key3; while I want it to be rejected because there are %key1; %key2 and #key4;.
I've do some search and the closest I can found is this question, but it returns similar result as my current regex.
What did i do wrong in my regex? What is the right regex?
Sorry if this is dumb question but I'm a newbie in regex.
The main thing needed are start ^ and end $ anchors. The rest can be simplified too:
^( *#\w+;)+$
See live demo.
Breaking it down:
^ = start
* = 0-n spaces
# = a literal hash (these don't need escaping in regex)
\w+ = one or more word characters (letters, digits and the underscore)`
$
If underscore can be in the input and must not be, then use:
^( *#[A-Za-z0-9]+;)+$
Your regex matches a full sentence because in your regex pattern(\B(\#[a-zA-Z0-9_; ]+\b)(\;)) you haven't specified where the matching process should start and end. So regex engine will try to match every position of the string on which you run the regex.match.
The way to specify where regex should try to match is done by adding anchors(^-beginning and $-end) to regex pattern.
You can edit your pattern to look like this: /(?:\s|^)(#[a-zA-Z0-9_; ]+?);(?:\s|$)/gm
Explanation:
/(?:\s|^)
- (?: means a non capture group, means dont include whatever is matched in between these () in the result. \s|^ means start matching if the beginning is a white space or beginning of a string.
(#[a-zA-Z0-9_; ]+);
- () is a regular capture group, which means that things captured in this group are included in the result.
You don't need to insert a '\' before every symbol
(?:\s|$)/
- another non capture group, specifying to match a white space or end position of a string.
gm
- global and multiline flags of javascript regex
Here is an example:
let regex_pattern = /(?:\s|^)(#[a-zA-Z0-9_; ]+);(?=\s|$)/gm
let input1 = " #key;" // string with just one word
let input2 = "#key1; #key2;#key3;" // string with one whole word and another word which will match your pattern
let input3 = "soemthing random #key;andjointstring" // a string with a word that will match the pattern but its not a whole word
console.log(input1.match(regex_pattern)) // it matches
console.log(input2.match(regex_pattern)) // it matches
console.log(input3.match(regex_pattern)) // it doesnt matches

fast regex match excluding certain words

I have the following regex pattern to find an email address in my code:
/[\._a-zA-Z0-9-]+#[\._a-zA-Z0-9-]{8,}/i
I want to make sure it does not match a certain string if it includes:
abc
xyz
Just to exclude the abc I have tried:
/(?!.*abc)[\._a-zA-Z0-9-]+#[\._a-zA-Z0-9-]{8,}/i
But that is horribly slow.
You need to "anchor" the regex to a position that can be found by the regex engine in an optimal way. The best way is to "tie" it to a word boundary position, and that should work here since emails start with word chars:
/\b(?!\S*abc)[\w.-]+#[\w.-]{8,}/i
BTW, [_a-zA-Z0-9] is equal to \w in JavaScript regex. Details:
\b - a word boundary
(?!\S*abc) - a negative lookahead that fails the match if there are zero or more non-whitespace chars and then abc immediately to the right of the current location
[\w.-]+ - one or more word, . or - chars
# - a # char
[\w.-]{8,}- eight or more word, . or - chars.
You can do it in two steps. Use a regular expression to find the email address, then check that it doesn't contain any of the prohibited strings.
if (preg_match('/[\._a-zA-Z0-9-]+#[\._a-zA-Z0-9-]{8,}/i', $text, $match) && !preg_match('/abc|xyz/i', $match[0])) {
$email = $match[0];
}

Kotlin / Regex - Replace a group of pattern with a repeating character

I would like to mask the email passed in the maskEmail function. I'm currently facing a problem wherein the asterisk * is not repeating when i'm replacing group 2 and and 4 of my pattern.
Here is my code:
fun maskEmail(email: String): String {
return email.replace(Regex("(\\w)(\\w*)\\.(\\w)(\\w*)(#.*\\..*)$"), "$1*.$3*$5")
}
Here is the input:
tom.cat#email.com
cutie.pie#email.com
captain.america#email.com
Here is the current output of that code:
t*.c*#email.com
c*.p*#email.com
c*.a*#email.com
Expected output:
t**.c**#email.com
c****.p**#email.com
c******.a******#email.com
Edit:
I know this could be done easily with for loop but I would need this to be done in regex. Thank you in advance.
For your problem, you need to match each character in the email address that not is the first character in a word and occurs before the #. You can do that with a negative lookbehind for a word break and a positive lookahead for the # symbol:
(?<!\b)\w(?=.*?#)
The matched characters can then be replaced with *.
Note we use a lazy quantifier (?) on the .* to improve efficiency.
Demo on regex101
Note also as pointed out by #CarySwoveland, you can replace (?<!\b) with \B i.e.
\B\w(?=.*?#)
Demo on regex101
As pointed out by #Thefourthbird, this can be improved further efficiency wise by replacing the .*? with a [^\r\n#]* i.e.
\B\w(?=[^\r\n#]*#)
Demo on regex101
Or, if you're only matching single strings, just [^#]*:
\B\w(?=[^#]*#)
Demo on regex101
I suggest keeping any char at the start of string and a combination of a dot + any char, and replace any other chars with * that are followed with any amount of characters other than # before a #:
((?:\.|^).)?.(?=.*#)
Replace with $1*. See the regex demo. This will handle emails that happen to contain chars other than just word (letter/digit/underscore) and . chars.
Details
((?:\.|^).)? - an optional capturing group matching a dot or start of string position and then any char other than a line break char
. - any char other than a line break char...
(?=.*#) - if followed with any 0 or more chars other than line break chars as many as possible and then #.
Kotlin code (with a raw string literal used to define the regex pattern so as not to have to double escape the backslash):
fun maskEmail(email: String): String {
return email.replace(Regex("""((?:\.|^).)?.(?=.*#)"""), "$1*")
}
See a Kotlin test online:
val emails = arrayOf<String>("captain.am-e-r-ica#email.com","my-cutie.pie+here#email.com","tom.cat#email.com","cutie.pie#email.com","captain.america#email.com")
for(email in emails) {
val masked = maskEmail(email)
println("${email}: ${masked}")
}
Output:
captain.am-e-r-ica#email.com: c******.a*********#email.com
my-cutie.pie+here#email.com: m*******.p*******#email.com
tom.cat#email.com: t**.c**#email.com
cutie.pie#email.com: c****.p**#email.com
captain.america#email.com: c******.a******#email.com

Regex for finding words with no or only one word between them

I need to find into multiple strings two words with no words or only one word between them. I created the regex for the case to find if those two words exist in string:
^(?=[\s\S]*\bFirst\b)(?=[\s\S]*\bSecond\b)[\s\S]+
and it works correctly.
Then I tried to insert in this regex additional code:
^(?=[\s\S]*\bFirst\b)(\b\w+\b){0,1}(?=[\s\S]*\bSecond\b)[\s\S]+
but it didn't work. It selects text with two or more words between searched words. It is not what I need.
First Second - must be selected
First word1 Second - must be selected
First word1 word2 Second - must be not selected by regex, but my regex select it.
Can I get advise how to solve this problem?
Root cause
You should bear in mind that lookarounds match strings without moving along the string, they "stand their ground". Once you write ^(?=[\s\S]*\bFirst\b)(\b\w+\b){0,1}(?=[\s\S]*\bSecond\b), the execution is as follows:
^ - the regex engine checks if the current position is the start of string
(?=[\s\S]*\bFirst\b) - the positive lookahead requires the presence of any 0+ chars followed with a whole word First - note that the regex index is still at the start of the string after the lookahead returns true or false
(\b\w+\b){0,1} - this subpattern is checked only if the above check was true (i.e. there is a whole word First somewhere) and matches (consumes, moves the regex index) 1 or 0 occurrences of a whole word (i.e. there must be 1 or more word chars right at the string start
(?=[\s\S]*\bSecond\b) - another positive lookahead that makes sure there is a whole word Second somewhere after the first whole word consumed with \b\w+\b - if any. Even if the word Second is the first word in the string, this will return true since backtracking will step back the word matched with (\b\w+\b){0,1} (see, it is optional), and the Second will get asserted, and [\s\S]+ will grab the whole string (Group 1 will be empty). See the regex demo with Second word word2 First string.
So, your approach cannot guarantee the order of First and Second in the string, they are just required to be present but not necessarily in the order you expect.
Solution
If you need to check the order of First and Second in the string, you need to combine all the checks into one single lookahead. The approach might turn out very inefficient with longer strings and multiple alternatives in the lookaround, consider either unrolling the patterns, or trying mutliple regex patterns (like this pseudo-code if /\bFirst\b/.finds_match().index < /\bSecond\b/.finds_match().index => Good, go on...).
If you plan to go on with the regex approach, you may match a string that contains First....Second only in this order:
^(?=[\s\S]*\bFirst(?:\W+\w+)?\W+Second\b)[\s\S]+
See the regex demo
Details:
^ - start of string
(?=[\s\S]*\bFirst(?:\W+\w+)?\W+Second\b) - there must be:
[\s\S]* - any zero or more chars up to the last
\bFirst - whole word First
(?:\W+\w+)? - optional sequence (1 or 0 occurrences) of 1+ non-word chars and 1+ word chars
\W+ - 1+ non-word chars
Second\b - Second as a whole word
[\s\S]+ - any 1 or more characters (empty string won't match).