(/\s+(\W)/g, '$1') - how are the spaces being removed? - javascript

let a = ' lots of spaces in this ! '
console.log(a.replace(/\s+(\W)/g, '$1'))
log shows lots of spaces in this!
The above regex does exactly what I want, but I am trying to understand why?
I understand the following:
s+ is looking for 1 or more spaces
(\W) is capturing the non-alphanumeric characters
/g - global, search/replace all
$1 returns the prior alphanumeric character
The capture/$1 is what removes the space between the words This and !
I get it, but what I don't get is HOW are all the other spaces being removed?? I don't believe I have asked for them to (although I am happy they are).
I get this one console.log(a.replace(/\s+/g, ' ')); because the replace is replacing 1 or more spaces between alphanumeric characters with a single space ' '.
I'm scratching my head to understand HOW the first RegEx /\s+(\W)/g, '$1'replaces 1 or more spaces with a single space.

What your regex says is "match one or more spaces, followed by one or more non-alphanumeric character, and replace that whole result with that one or more non-alphanumeric character". The key is that the \s+ is greedy, meaning that it will try and match as many characters as possible. So in any given string of spaces it will try and match all of the spaces it can. However, your regex also requires one or more non-word characters (\W+). Because in your case the next character after each final space is a word character (i.e. a letter), this last part of the regex must match the last space.
Therefore, given the string a b, and using parens to mark the \s+ and \W+ matches, a( )( )b is the only way for the regex to be valid (\s+ matches the first two spaces and \W+ matches the last space). Now it's just a simple substitution. Since you wrapped the \W+ in parentheses that makes it the first and only capturing group, so replacing the match with $1 will replace it with that final space.
As another example, running this replace against a !b will result in the match looking like a( )(!)b (since ! is now the last non-word character), so the final replaced result will be a!b.

Lets take this string 'aaa &bbb' and run it through.
We get 'aaa&bbb'
\s+ grabs the 3 spaces before the ampersand
(\W) grabs the ampersand
$1 is the ampersand and replaces ' &' with '&'
That same principal applies to the spaces. You are forcing one of the spaces to satisfy the (\W) capture group for the replacement. It's also why your exclamation point isn't nuked.

List of matches would be the following. I replaced space with ☹ so it is easier to see
"☹☹☹☹(☹)",
"☹☹☹☹(☹)",
"☹☹(!)",
"☹(☹)"
And the code is saying to replace the match with what is in the capture group.
' lots of☹☹☹☹(☹)spaces☹☹☹☹(☹)in this☹☹(!)☹(☹)'
so when you replace it you get
' lots of☹spaces☹in this!☹'

Related

Regex for any character except quote after comma

I want to match every word separated by comma, but it must not include a quote like ' or ".
I was using this regex:
^[a-zA-Z0-9][\!\[\#\\\:\;a-zA-Z0-9`_\s,]+[a-zA-Z0-9]$
However, it only matches a character and number and not a symbol.
The output should be:
example,example //true
exaplle,examp#3 //true, with symbol or number
example, //false, because there is no word after comma
,example //false, because there is no word before comma
##example&$123,&example& //true, with all character and symbol except quote
You can match 1+ times what is present in the character class. Then repeat 1+ times in a non capturing group (?: what is present in the character class, preceded by a comma.
^[!\[#\\:;a-zA-Z0-9`_ &$#]+(?:,[!\[#\\:;a-zA-Z0-9`_ &$#]+)+$
Regex demo
Note that you don't have to escape \!, \#, \: and \; in the character class, and that \s might also possibly match a newline.
I'm assuming you want the whole string to match perfectly with your conditions and return true then and then only.
These are the conditions-
Each word should be separated by a comma, said comma should have 2 valid words on each side
Words can contain anything except the 2 kinds of quotes (' and ") and whitespace characters (spaces and newlines).
The regex you would use is this- ^(?:[^,'"\s]+,[^,'"\s]+)+$, with the global flag (g) on.
Check out the demo here
Edit: As per request of being able to match only a single word.
This is the regex you would use for that- ^(?:(?:[^,'"\s]+,[^,'"\s]+)+|[^,'"\s]+)$
This will match words separated by a , as well as match just a single word.
The conditions for what qualifies as a word remains the same as aforementioned.
Quick explanation:-
^[^,'"\s]+,[^,'"\s]+$
This part matches 2 words separated by a comma, [^,'"\s]+ denotes a word
Wrapping that whole thing in ^(?:[^,'"\s]+,[^,'"\s]+)+$ simply makes it repeat, so it'll match N number of words separated by a comma, not just 2
Then adding another alternative using | and wrapping the whole thing in a group (non-capturing), we get ^(?:(?:[^,'"\s]+,[^,'"\s]+)+|[^,'"\s]+)$
This simply just adds the alternative [^,'"\s]+ - which matches a singular word.
Check out the updated demo here

\b regex for end of word [duplicate]

I'm attempting to match the last character in a WORD.
A WORD is a sequence of non-whitespace characters
'[^\n\r\t\f ]', or an empty line matching ^$.
The expression I made to do this is:
"[^ \n\t\r\f]\(?:[ \$\n\t\r\f]\)"
The regex matches a non-whitespace character that follows a whitespace character or the end of the line.
But I don't know how to stop it from excluding the following whitespace character from the result and why it doesn't seem to capture a character preceding the end of the line.
Using the string "Hi World!", I would expect: the "i" and "!" to be captured.
Instead I get: "i ".
What steps can I take to solve this problem?
"Word" that is a sequence of non-whitespace characters scenario
Note that a non-capturing group (?:...) in [^ \n\t\r\f](?:[ \$\n\t\r\f]) still matches (consumes) the whitespace char (thus, it becomes a part of the match) and it does not match at the end of the string as the $ symbol is not a string end anchor inside a character class, it is parsed as a literal $ symbol.
You may use
\S(?!\S)
See the regex demo
The \S matches a non-whitespace char that is not followed with a non-whitespace char (due to the (?!\S) negative lookahead).
General "word" case
If a word consists of just letters, digits and underscores, that is, if it is matched with \w+, you may simply use
\w\b
Here, \w matches a "word" char, and the word boundary asserts there is no word char right after.
See another regex demo.
In Word text, if I want to highlight the last a in para. I search for all the words that have [space][para][space] to make sure I only have the word I want, then when it is found it should be highlighted.
Next, I search for the last [a ] space added, in the selection and I will get only the last [a] and I will highlight it or color it differently.

Explanation of this Regular expression

I'm not very good with Regular Expressions, and I didn't fully understood this one, All I get from this is that it find every h1 and add a class to it's last word.
$("h1").html(function(index, old) {
return old.replace(/(\b\w+)$/, '<span class="myClass">$1</span>');
});
I'm trying to make it work by last two characters
Here is and explanation:
/ : regex delimter
( : begin capture group #1
\b : word boundary
\w+ : one or more word character (same as [a-zA-Z0-9_]+)
) : end of group
$ : end of string
/ : regex delimiter
It matches the last word of the string, ie the last word of the h1 tag.
This (poorly written) regex finds a sequence of word characters (latin letters, numbers and underscore) at the end of the input. The same can be achieved much simpler: /\w+$/, so neither \b nor parens are actually necessary here.
To match two last words you'll need something like
/\w+(?=(\W+\w+)?$)/g
which means "a word, optionally followed by another word before the end of the input".
To match two last characters -- well, this is something you should be able to figure out on your own (hint: any character is . (dot) in regex language).

what's the meaning of the below regex in javascript

data.replace(/(.*)/g, '$1')
I encountered the above in smashing nodejs, can someone quickly explain this syntax? I'm new to Regex.
. means match characters except new line.
* matches 0 or more of the preceeding token. This is a greedy match, and will match as many characters as possible before satisfying the next token.
$1 refers to the matched group.
g modifier means global, which in turn means,
"don't stop at the first match. Continue to match even after that"
Basically what it is doing is capturing every character into a group until it encounters a \n(newline) and replacing it with the same.
There is no change in this operation and you should avoid doing this.
. can be any character, except the newline character, and * quantifier means that . can be matched 0 to unlimited times. So, it matches all the characters in the data. The parenthesis around .*, group all the matched characters into a group and $1 refers to the first captured group. So, we basically match all the characters and replace that with the matched characters.
It is similar to doing
str.replace(str1, str1)
You found it in "Smashing Node.js". I tried and found it too. There is the code: data.replace(/(.*)/g, ' $1') there. Please notice the two leading spaces before $1. It makes the indentation of the whole text.
.* matches the whole line,
replaces it with " " + the same line,
repeats it until eof because g modifier is there

/(\S)\1(\1)+/g matching all occurrences of three equal non-whitespace characters following each other

Its given: /(\S)\1(\1)+/g matches all occurrences of three equal non-whitespace characters following each other.
I don't understand why there is () around (\S) and 2nd (\1), but not around 1st (\1). Can anyone help in explaining how above regex works?
src: http://www.javascriptkit.com/javatutors/redev2.shtml
Thnx in advance.
The \S needs parentheses to capture its value, so you can refer back to the captured value with \1. \1 means "match the same text which capturing group #1 matched".
I believe there is a problem with this regex. You said you want to match "three equal non-whitespace characters". But the + will make this match 3 or more equal, consecutive non-whitespace characters.
The g on the end means "apply this regex over the entire input string, or globally".
The second set of parentheses is not necessary. It needlessly captures the repeated character a second time, while matching the same strings as this regex:
/(\S)\1\1+/g
Also, as #AlexD pointed out, the description should say that it matches at least three characters. If you replaced that regex with BONK in the string fooxxxxxxbar:
'fooxxxxxxbar'.replace(/(\S)\1\1+/g, 'BONK')
..you might expect the result to be fooBONKBONKbar from their description, because there are two sets of three 'x's. But in fact the result would be fooBONKbar; the first \1 matches the second 'x', and the \1+ matches the third 'x' and any 'x's that follow it. If they wanted to match just three characters, they should have left the + off.
I noticed several other sloppy descriptions like that, plus at least one outright error: \B is equivalent to (?!\b) (a position that's not a word boundary), not [^\b] (a character that's not a backspace). For that matter, their description of word boundaries--"the position between a word and a space"--is wrong, too. A word boundary isn't defined by any particular character, like a space--in fact, it can just as well be the absence of any character that creates one. The string:
Word
...starts with a word boundary because 'W' is a word character and, being first, it's not preceded by another word character. Similarly, the 'd' is not followed by another word character, so the end of the string is also a word boundary.
Also, a regex doesn't know from words, only word characters. The definition of a word character can vary depending on the regex flavor and Unicode or locale settings, but it always includes [A-Za-z0-9_] (ASCII letters and digits plus the underscore). A word boundary is simply a position that's between one of those characters and any other character (or no other character, as I explained earlier).
If you want to learn about regexes, I suggest you forget that site and start here instead: regular-expressions.info.

Categories