1

I am trying to compose a regexp in JS for RGB color string with optional opacity value. To get the data for each channel and opacity I am capturing named groups.

Everything works as intended, but there is something strange for me. When the "B" value in range [200-255], the last digit always goes to opacity group...

For example:

rgb 195 230 199 -> r: 195, g: 230, b: 199 (All is fine)

rgb 195 230 199 0.25 -> r: 195, g: 230, b: 199, opacity: 0.25 (All is fine)

rgb 195 230 200 -> r: 195, g: 230, b: 20, opacity: 0 (Not fine)

And this strange behavior works the same for range 200-255. And spend literally and hour trying to solve it, please, I need some help.

The full regexp is:

^(?:rgb|rgba)?[\s+\/]*\(?[\s+\/]*(?<r>[01]?\d\d?|2[0-4]\d|25[0-5])[\s+\/]+(?<g>[01]?\d\d?|2[0-4]\d|25[0-5])[\s+\/]+(?<b>[01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])(?:\s)?(?<opacity>0\.\d{1,2}|[1-9]\d{0,1}(?!\d)|100|0|1)??(?:\%)?\)?$

Broken into parts:

^
(?:rgb|rgba)?
[\s+\/]*
\(?
[\s+\/]*
(?<r>[01]?\d\d?|2[0-4]\d|25[0-5])
[\s+\/]+
(?<g>[01]?\d\d?|2[0-4]\d|25[0-5])
[\s+\/]+
(?<b>[01]?\d\d?|2[0-4]\d|25[0-5])
(?:\s)?
(?<opacity>0\.\d{1,2}|[1-9]\d{0,1}(?!\d)|100|0|1)??
(?:\%)?
\)?
$
4
  • 1
    You could add a word boundary before the opacity group, and shorten the pattern a bit ^(?:rgba?)?[\s\/]*\(?[\s\/]*(?<r>[01]?\d\d?|2[0-4]\d|25[0-5])[\s\/]+(?<g>[01]?\d\d?|2[0-4]\d|25[0-5])[\s+\/]+(?<b>[01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\s?\b(?<opacity>0\.\d{1,2}|[1-9]\d?(?!\d)|100|0|1)?%?\)?$ regex101.com/r/9dxpVj/1 Commented Jul 7, 2020 at 14:31
  • Those 4 lines of regex you could've spent for some .match() / .split()/.join() and save a handful of CPU cycles. Commented Jul 7, 2020 at 14:33
  • Or you could also make the opacity part with the leading space optional regex101.com/r/oXJqCp/1 Commented Jul 7, 2020 at 14:35
  • @YevgenGorbunkov Yes, you are definitely right. I have to do this way because I build up a lot of regular expressions programmatically from "common" parts to make it readable. At least I think so :D Commented Jul 7, 2020 at 18:02

1 Answer 1

1

In the last part you are matching an optional whitespace char (?:\s)?

This value will not match rgb 195 230 200 if the whitespace char is not optional.

But as it is optional it can match 20 in the <b> group from the alternation and match 0 in the <opacity> group from that alternation.


You could either place a word boundary before the opacity group, or make the group optional.

^(?:rgba?)?[\s\/]*\(?[\s\/]*(?<r>[01]?\d\d?|2[0-4]\d|25[0-5])[\s\/]+(?<g>[01]?\d\d?|2[0-4]\d|25[0-5])[\s+\/]+(?<b>[01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])(?: (?<opacity>0\.\d{1,2}|[1-9]\d?(?!\d)|100|0|1)?%?)?\)?$

Regex demo

Note that the pattern can be shortened a bit by omitting superfluous groups and escapes like (?:\%)?and using ? which is the same as {0,1}

In this part [\s+\/]* the pattern allows matching a + character instead of repeating the \s 1 or more times because it is a character class. If that is not the intention, you can omit the +

Sign up to request clarification or add additional context in comments.

1 Comment

I didn't know I can do that with named group. Thank you for help and making my regexp better! You saved me a lot of time and gave me some knowledge, thank you!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.