RegEx matching help: won't match on each appearence - javascript

I need to write a little RegEx matcher which will match any occurrence of strings in the form of
[a-zA-Z]+(_[a-zA-Z0-9]+)?
If I use the regex above it does match the sections needed but would also match onto the abc part of 4_abc which is not intended. I tried to exclude it with:
(?:[^a-zA-Z0-9_]|^)([a-zA-Z]+(_[a-zA-Z0-9]+)?)(?:[^a-zA-Z0-9_]|$)
The problem is that the 'not' matches at the beginning and end are not really working like I hoped they would. If I use them on the example
a_d Dd_da 4_d d_4
they would block matching the second Dd_da because the space was used in the first match.Sadly I can't use lookarounds because I am using JS.
So the input:
a_d Dd_da 4_d d_4
should match: a_d, Dd_da and d_4
but matches: a_d (there is a space at the end)
Is there another way to match the needed sections, or to not consume the 'anchor' matches?
I really appreciate your help.

You can make use of \b:
\b[a-zA-Z]+(_[a-zA-Z0-9]+)?\b
\b matches the (zero-width) point where either the preceding character or following character is a letter, digit or underscore, but not both. It also matches with the start/end of the string if the first/last character is a letter, digit or underscore.

Related

How to not match given prefix in RegEx without negative lookbehind?

Goal
The goal is matching a string in JavaScript without certain delimiters, i.e. a string between two characters (the characters can be included in the match).
For example, this string should be fully matched: $ test string $. This can appear anywhere in a string. That would be trivial, however, we want to allow escaping the syntax, e.g. The price is 5\$ to 10\$.
Summarized:
Match any string that is enclosed by two $ signs.
Do not match it if the dollar signs are escaped using \$.
Solution using negative lookbehind
A solution that achieves this goal perfectly is: (?<!\\)\$(.*?)(?<!\\)\$.
Problem
This solution uses negative lookbehind, which is not supported on Safari. How can the same matches be achieved without using negative lookbehind (i.e. on Safari)?
A solution that partially works is (?<!\\)\$(.*?)(?<!\\)\$. However, this will also match the character in front of the $ sign if it is not a \.
You might rule out what you don't want by matching it, and capture what you want to keep in group 1
\\\$.*?\$|\$.*?\\\$|(\$.*?\$)
Regex demo
You may use this regex and grab your inner text using capture group #1 as you are already doing in your current regex using lookbehind:
(?:^|[^\\])\$((?:\\.|[^$])*)\$
RegEx Demo
RegEx Details:
(?:^|[^\\]): Match start position or a non-backslash character in a non-capturing group
\$: Match starting $
(: Start capturing group
(?:\\.|[^$])*: Match any escaped character or a non-$ character. Repeat this group 0 or more times
): End capturing group
\$: Match closing $
PS: This regex will give same matches as your current regex: (?<!\\)\$(.*?)(?<!\\)\$

Replacements only in the first line with a regex

There is a transform of multiline string.
!a! b!
should become
.a. b.
And
!a! b!
c!
!d!
should become
.a. b.
c!
!d!
I approached it with a lookbehind:
str(/(?<!\n)([^\n!]*)!+/g, '$1.')
It didn't work as intended:
.a. b.
c.
!d.
Splitting a string and transforming the first line seems straightforward. But is there a reliable way to do replacements only in the first line of multiline string with a regex only?
Also would appreciate an explanation what exactly goes wrong with my approach so it fails.
The question is not limited to JS regex flavour but I'm interested in this one in the first place.
About the pattern you tried:
(?<!\n) Negative lookbehind, assert what is directly to the left is not a newline or !
([^\n!]*) Capture group 1, match 0+ times any char except a newline or !
!+ Match 1+ times ! (What you want to remove)
The pattern will match too much, as it will match all the individual parts. There is for example no rule that says match this pattern 2 times, so you will replace with group 1 for every time that pattern has a match.
Note that the quantifier in this part is 0+ times ([^\n!]*) it will also match a single ! except when preceded by a newline.
If you can make use of SKIP FAIL, you can first match what you want to avoid, which in this case is a line that optionally starts with an exclamation mark and ends with an exclamation mark with none in between.
After that match all the other exclamation marks and replace them with a dot.
^!?[^\r\n!]*!$(*SKIP)(*FAIL)|!
See a regex demo
Another option could be using 2 capturing groups.
The first group will match between the first set of exclamation marks, and the second group will match the whitespaces after followed by a char other than !.
Then match the ! at the end so it is not in the replacement
!([^\s!]+)!([^\S\r\n]+[^\s!])!
See another regex demo
In the replacement use the 2 capturing groups with the dots
.$1.$2.

How to make regex for 3 slashes?

I want to write regex for following
students/ad34567-06c1-498c-9b15-cdbac695c1f2/data/sessions
Where students, data and sessions should be exact match.
i have tried this
[students]\[a-z]\[a-z]\[a-z]
You can try this regex, although your question is not clear to me.
^students\/([\w\-\d]+)\/data\/sessions$
Check here https://regex101.com/r/xnxwCX/1
you can grab the data in between students/, /data/session.
In your regex [students]\\[a-z]\\[a-z]\\[a-z] you are trying to match with word students in a character class [students] which will match one of the specified characters instead of matching the whole word.
To match a forward slash you have to use \/ instead of //. [a-z] is specified without a quantifier and will match 1 character from a-z.
To match your example string you might use
^students\/[a-z0-9]+(?:-[a-z0-9]+)+\/data\/sessions$
Regex demo
This part [a-z0-9]+(?:-[a-z0-9]+)+ matches one or more times a lowercase character or a digit [a-z0-9]+
Following a non capturing group repeated one or more times that will match a hyphen followed by matching one or more times a lowercase character or a digit (?:-[a-z0-9]+)+
You might also use [a-f0-9] if your characters are a -f

Regex to match all spaces, except single spaces having words on both sides

I have come so far:
1) Run regex / /g to match all spaces.
2) Run a new call to regex /\b( )\b/g to match the spaces that need to be excluded.
Now I need them both fused in one statement. All spaces except the ones returned by the second. Any help?
Live regex for testing: https://regex101.com/r/26w2WR/1
EDIT: Although good answers are already available, I found that trying to match "words" with \b or \B is not always a good idea, as a lot of printable characters like dots and quotes are not seen as words by RegEx. Another problem is when you are looping through DOM nodes, sometimes you encounter inline styling tags like <strong> which should also just count as a beginning/end of a word, but a #text node just ends before the tag. So you may want to include start & end of a string in the RegEx too. For anyone wishing to address these too, I ended up with this RegEx:
/(\S|^)( )(?=\S|$)/g
This uses \S (not white space), inlcudes start/end of a string and applies groups for replacement ability. Replace JS looks like this:
yourTextNode.replace(/(\S|^)( )(?=\S|$)/g, '$1'+ yourreplacement)
To match chars, you can use (\u00A0) instead of ( )
Hope this helps.
You can use negative look-ahead:
(?!\b \b)( )
Without any look-around you can use this regex with \B and alternation:
\B +| +\B
Updated RegEx Demo
\B assert position where \b does not match
Above matches a space that is preceded or followed by \B

Regular expression match array of words excluding words found in contractions

Say I have an array of words, for example: (hi|ll|this|that|etc) and I want to find it in the following text:
Hi, I'll match this and ll too
I'm using: \\b(hi|ll|this|that|etc)\\b
But I want to only match whole words, excluding words found in contractions. Basically treat apostrophes as another "word seperator". In this case, it shouldn't match the "ll" in "I'll".
Ideas?
Use the apostrophe in addition to \b to begin and end a match:
(?:\b|')(hi|ll|this|that|etc)(?:\b|')
(?:...) means a non-capturing group. Stub on Regex101
If you want match just words you can try with:
(?:^|(?=[^']).\b)(hi|ll|th(?:is|at)|etc)\b
DEMO
and get words with group 1. However the \b will still allow to match fragments like: -this or #ll. I don't know is it desired result.

Categories