6

Currently I have this regex which matches to an RGB string. I need it enhanced so that it is robust enough to match either RGB or RGBA.

rgbRegex = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/; //matches RGB

http://jsfiddle.net/YxU2m/

var rgbString =  "rgb(0, 70, 255)";
var RGBAString = "rgba(0, 70, 255, 0.5)";

var rgbRegex = /^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/;
//need help on this regex
//I figure it needs to be ^rgba?, and then also an optional clause to handle the opacity

var partsRGB = rgbString.match(rgbRegex);
var partsRGBA = RGBAString.match(rgbRegex);

console.log(partsRGB); //["rgb(0, 70, 255)", "0", "70", "255"]
console.log(partsRGBA); //null. I want ["rgb(0, 70, 255, 0.5)", "0", "70", "255", "0.5"] 
fortuneRice
  • 4,214
  • 11
  • 43
  • 58
  • 1
    http://stackoverflow.com/questions/638948/background-color-hex-to-javascript-variable-jquery/639030#639030 – nickf Sep 25 '11 at 05:30
  • 1
    That's going to have a lot of false negatives. Spaces are allowed before commas and percentage values are allowed instead of decimals. – Quentin Sep 25 '11 at 08:38

17 Answers17

9

It's not so simple- an rgb is illegal with a fourth parameter. You also need to allow for percentage decimals as well as integer values for the rgb numbers. And spaces are allowed almost anywhere.

function getRgbish(c){
    var i= 0, itm,
    M= c.replace(/ +/g, '').match(/(rgba?)|(\d+(\.\d+)?%?)|(\.\d+)/g);
    if(M && M.length> 3){
        while(i<3){
            itm= M[++i];
            if(itm.indexOf('%')!= -1){
                itm= Math.round(parseFloat(itm)*2.55);
            }
            else itm= parseInt(itm);
            if(itm<0 || itm> 255) return NaN;
            M[i]= itm;
        }
        if(c.indexOf('rgba')=== 0){
            if(M[4]==undefined ||M[4]<0 || M[4]> 1) return NaN;
        }
        else if(M[4]) return NaN;
        return M[0]+'('+M.slice(1).join(',')+')';
    }
    return NaN;
}

//testing:

var A= ['rgb(100,100,255)',
'rgb(100,100,255,.75)',
'rgba(100,100,255,.75)',
'rgb(100%,100%)',
'rgb(50%,100%,0)',
'rgba(100%,100%,0)',
'rgba(110%,110%,0,1)'];

A.map(getRgbish).join('\n');

returned values:
rgb(100,100,255)
NaN
rgba(100,100,255,.75)
NaN
rgb(127,255,0)
NaN
NaN
kennebec
  • 102,654
  • 32
  • 106
  • 127
  • _rgb is illegal with a fourth parameter_ - As of CSS Colors Level 4, rgba() is an alias for rgb(). In browsers that implement the Level 4 standard, they accept the same parameters and behave the same way. [Source](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#rgb()_and_rgba()) – comesuccingfuccslot Jul 09 '18 at 13:42
8

Will this do?

var rgbRegex = /^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/
Rusty Fausak
  • 7,355
  • 1
  • 27
  • 38
  • This one will return `rgb(0,0,0, 0.5);` valid - which is **not** a valid **rgb** color. – Roko C. Buljan Jul 06 '15 at 23:38
  • Thanks for the help. /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)/gmi; Works great. rgba(255, 240, 71, 0.22); – Gregory Bologna Oct 23 '19 at 22:15
  • This one fails for "rgba(0, 255, 0, .5)" – WebTigers Jun 05 '21 at 09:59
  • That is a completely valid CSS RGB value, Roko, at least in Firefox and Chromium browsers. Which is the problem I'm having, none of these seem to be good for CSS RGB values, at least. All of these are technically acceptable CSS RGB values: `rgb( 0, 255 , 255, .4555 )` `rgba( 0%, 255% , 255%, .4555% )` `rgb( 141414 2551212 25521 / 99999% )` `rgba( 2323233, .5533, .353535, 3535353 )` The problem I'm having for example, is that if one group has % color components, all 3 rgb components need to have it. I'm not that good at regex though. :( – Dustin Perolio Jan 11 '22 at 20:40
6

I made a regex that checks for rgb() and rgba() values. It checks for 3 tuples of 0-255 and then an optional decimal number between 0-1 for transparency. TLDR;

rgba?\(((25[0-5]|2[0-4]\d|1\d{1,2}|\d\d?)\s*,\s*?){2}(25[0-5]|2[0-4]\d|1\d{1,2}|\d\d?)\s*,?\s*([01]\.?\d*?)?\)

Broken into the different parts we have:

rgba?\( // Match rgb( or rgba( as the a is optional

0-255 is matched with alternation of:

\d\d? // Match 0-99

1\d\d // Match 100 - 199

2[0-4]\d // Match 200-249

25[0-5] // Match 250 - 255

The handling of comma and space around the 0-255 tuples takes some space. I match 0-255 tuples with mandatory trailing comma and optional spaces twice

(25[0-5]|2[0-4]\d|1([0-9]){1,2}|\d\d?)\s*,\s*){2}

Then a 0-255 tuple with optional comma and space - to allow for rgb() values without the trailing comma

(25[0-5]|2[0-4]\d|1([0-9]){1,2}|\d\d?),?\s*

Then comes an optional 0-1 as whole number or decimal number:

([01]\.?\d*?)? // 0 or 1 needed, dot and decimal numbers optional

And we end with a closing bracket

\)
Pistus
  • 381
  • 2
  • 8
  • 1
    Would be better to explain like: `25[0-5]` = *match numbers from 250 to 255*. `2[0-4]\d` = *Match numbers from 200 to 249*. `1\d\d` = *match numbers from 100 to 199*. `\d\d?` = *match any number from 0 to 99 (the `?` makes the second digit optional)*. The way you explained it is wrong: *`25[0-5] // If tripple digit starting with 25 then last number must be 0 `* is incorrect so is `2[0-4]\d // If tripple digit starting with 2, second digit must be 0-5` an incomplete description. ;) – Roko C. Buljan Jul 15 '15 at 05:04
  • 1
    To add to, `,\s?` is inadequate. `rgb/a` can have as many spaces as you want before the comma and after the comma, so `\s*,\s*` would be the correct way. – Roko C. Buljan Jul 15 '15 at 05:09
  • Updated answer with your improvements @RokoC.Buljan. Much easier way to explain the number matching:) – Pistus Jul 15 '15 at 13:03
  • regarding the alpha part: fractions could start with `.`, for examlpe "rgba(0,0,0,.5)" is valid. the `[01]` is actually optional. does this makes sense to you: `([01\.]\.?\d*?)?` – oriadam May 04 '21 at 07:20
  • This is still not working. It captures the ", " of the first matching group. – WebTigers Jun 05 '21 at 09:55
  • Indeed, not sure why people have upvoted this, it misses the first number in many cases – Ian Jun 30 '21 at 15:56
6

Try the following script for RGBA value, the result is an object.

       var getRGBA = function (string) {
          var match = string.match(/^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(\d*(?:\.\d+)?)\)$/);
          return match ? {
            r: Number(match[1]),
            g: Number(match[2]),
            b: Number(match[3]),
            a: Number(match[4])
          } : {}
        };
                           
        var rgba = getRGBA('rgba(255, 255, 255, 0.49)');
        console.log(rgba);
GibboK
  • 71,848
  • 143
  • 435
  • 658
3

Fun way to spend an evening!

Code

Here it is:

/^rgba?\(\s*(?!\d+(?:\.|\s*\-?)\d+\.\d+)\-?(?:\d*\.\d+|\d+)(%?)(?:(?:\s*,\s*\-?(?:\d+|\d*\.\d+)\1){2}(?:\s*,\s*\-?(?:\d+|\d*\.\d+)%?)?|(?:(?:\s*\-?\d*\.\d+|\s*\-\d+|\s+\d+){2}|(?:\s*\-?(?:\d+|\d*\.\d+)%){2})(?:\s*\/\s*\-?(?:\d+|\d*\.\d+)%?)?)\s*\)$/i

This regex isn't case-insensitive for rgba/RGBA so we should probably keep the i flag when running.

192 characters!

To avoid negative lookaheads and to only roughly match more common inputs, try

/^rgba?\(\d+(?:(?:\s*,\s*\d+){2}(?:\s*,\s*(?:\d*\.\d+|\d+)%?)?)|(?:(?:\s+\d+){2}(?:\s*\/\s*(?:\d*\.\d+|\d+)%?)?)\)$/

Note that this is only presently useful for testing validity. Adding reliable capture groups would lengthen and complicate it past a level I'm comfortable hand-rolling. We can extract the numbers after validating with something like:

regexStr
  .match(/\((.+)\)/)[1]
  .trim()
  .split(/\s*[,\/]\s*|\s+/)

Background

The purpose of this code is particularly to match the current CSS allowed rgb/rgba values. Might not fit everyone's use case, but that's what the Bounty was for.

Since posting, CSS now allows rgb(R G B / A). It also allows percentages, negatives, and decimals for all values. These are therefore valid:

✔ rgb(1, -2, .3, -.2)
✔ rgb(1 -2 .3 / -.2)
✔ rgb(1 -2 .3 / -.2%)
✔ rgb(1% -2% .3% / -.2%)
✔ rgb(1% -2% .3% / -.2)

When using percentages, all three color values must be percentages as well. The alpha can be a percentage in any environment.

While writing this, I also found an area where implementing this was quite difficult with regex.

✔ rgb(1 .2.3)
✔ rgb(1-.2.3)
✘ rgb(1.2.3)
✘ rgb(1 -2.3)

The bottom 2 examples are false when using CSS.supports(color, str). Essentially, if it looks like it's possible that the rgb() only contains 2 values, it will register as invalid.

We can just handle this directly as a special case by creating a variable-length negative lookahead. This may be important to realize if we want to transfer this regex to another engine.

(?!\d+(?:\.|\s*\-?)\d+\.\d+)

It just rejects early on matches for 1.2.3, 1 2.3, and 1 -2.3.

Code Walkthrough

This is a massive one, so I'll take it apart, piece-by-piece. I'm going to pretend we're dealing with Extended Mode and so I'll litter the regex with whitespace and comments to make things clearer.

^rgba?\(\s*
    (?!\d+(?:\.|\s*\-?)\d+\.\d+)   # negative lookahead
    \-?(?:\d*\.\d+|\d+)            # handle 1,-1,-.1,-1.1
    (%?)                           # save optional percentage
  • To start, we make the a optional and allow whitespace after the parentheses.
  • We add our negative lookahead for the 2 special cases.
  • We then match our first number. Our number can be an integer, fractional, and possibly negative.
  • We capture the optional percentage. This is to save on characters later by taking advantage of backreferences. We save about 60 characters with this.
(?: # next 2-3 numbers either
    (?: # 2 separated by commas
        \s*,\s*
        \-?(?:\d+|\d*\.\d+)
        \1
    ){2}
    (?: # optional third maybe-percentage
        \s*,\s*
        \-?(?:\d+|\d*\.\d+)
        %?
    )?
  • Next, capture comma separated values.
  • \1 refers to the % if it existed earlier
  • Allow our third number to have a percentage irrespective of whether the previous numbers hade one.
|(?: # match space-separated numbers
    (?: # non-percentages
        \s*\-?\d*\.\d+  # space-optional decimal
        |\s*\-\d+       # space-opt negative
        |\s+\d+         # space-req integer
    ){2}
    |(?: # or percentages
        \s*
        \-?(?:\d+|\d*\.\d+)
        %
    ){2}
)
(?: # optional third maybe-percentage
    \s*\/\s*
    \-?(?:\d+|\d*\.\d+)
    %?
)?
  • First try matching non percentage numbers separated by either ., -, or whitespace.
  • If no match, try percentage numbers, space optional
  • Optionally match the alpha value, separated by /
EmNudge
  • 81
  • 4
  • 1
    Thanks Em, it's a little unfortunate about using negative lookahead, but completely understandable. This is probably the most complete rgb regex that has ever been made in terms of acceptable CSS values, and it covers all the bases I can think of. And I appreciate that you've even take the time to give a clear and detailed description of the process. Well done! – Dustin Perolio Jan 13 '22 at 02:23
2

For patterns: rbga(12,123,12,1) rbga(12,12,12, 0.232342) rgb(2,3,4)

/^(rgba|rgb)\(\s?\d{1,3}\,\s?\d{1,3}\,\s?\d{1,3}(\,\s?(\d|\d\.\d+))?\s?\)$/
2

You can use this regex:

var regex = /(^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6})$|^(rgb|hsl)a?\((\s*\/?\s*[+-]?\d*(\.\d+)?%?,?\s*){3,5}\)$)/igm;

Example:

function myFunction() {
    var myRegex = /(^#([0-9a-f]{3}|[0-9a-f]{4}|[0-9a-f]{6})$|^(rgb|hsl)a?\((\s*\/?\s*[+-]?\d*(\.\d+)?%?,?\s*){3,5}\)$)/igm;
    var el = document.getElementById('input');
    document.getElementById('value').textContent = myRegex.test(el.value);

}
<input id="input" />
<button onclick="myFunction()">Submit</button>
<br />
<p id="value"></p>

Should match:

  • #111
  • #1111
  • #222222
  • rgb(3,3,3)
  • rgba(4%,4,4%,0.4)
  • hsl(5,5,5)
  • hsla(6,6,6,0.6)
Nak
  • 251
  • 1
  • 6
  • `rgb(2,2,2,2)` and `rgba(3,3,3,33)` are actually valid CSS colors. `rgb( 0, 255 , 255, .4555 )` `rgba( 0%, 255% , 255%, .4555% )` `rgb( 141414 2551212 25521 / 99999% )` `rgba( 2323233, .5533, .353535, 3535353 )` are as well, and do not pass the test here. – Dustin Perolio Jan 11 '22 at 20:48
  • @DustinPerolio Thanks for your comments, I've tweaked it a few places to accommodate the diverse CSS support – Nak Jan 13 '22 at 04:11
2

If you need to be strict, i.e. rule out rgb(0, 70, 255, 0.5), you need to fuse both cases together with | :

var rgbRegex = /(^rgb\((\d+),\s*(\d+),\s*(\d+)\)$)|(^rgba\((\d+),\s*(\d+),\s*(\d+)(,\s*\d+\.\d+)*\)$)/; 

http://jsfiddle.net/YxU2m/2/

mihai
  • 37,072
  • 9
  • 60
  • 86
  • this /(^rgb\((\d+),\s*(\d+),\s*(\d+)\)$)|(^rgba\((\d+),\s*(\d+),\s*(\d+)(,\s*\d+(\.\d+)?)*\)$)/i will allow rgba color's with a decimal in the alpha like "rgba(0, 0, 0, 0)" – hobberwickey Mar 15 '17 at 19:20
1

i use this it is help

(.*?)(rgb|rgba)\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)/i

if you check rgba(102,51,68,0.6)

it will return

1.  [0-0]   ``
2.  [0-4]   `rgba`
3.  [5-8]   `102`
4.  [9-11]  `51`
5.  [12-14] `68`
6.  [15-18] `0.6`

and if you check rgb(102,51,68)

it will return

1.  [21-21] ``
2.  [21-24] `rgb`
3.  [25-28] `102`
4.  [29-31] `51`
5.  [32-34] `68`
vipmaa
  • 1,022
  • 16
  • 25
1

This regex is a good compromise between the complexity of the regex and number of use-cases covered.

/(rgb\(((([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]),\s*){2}([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\)))|(rgba\(((([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]),\s*){3}(1|1.0*|0?.\d)\)))/

rgb and rgba need to be treated differently as one needs the 4th argument and one doesn't.

This regex takes that into account. It also deals with:

  • 1, 2 or 3 digit rgb values
  • rgb values under 0 and over 255
  • (some) spaces
  • a 4th value missing from rgba
  • a 4th value in rgb

This regex does not take into account:

  • every legal type of spacing
  • percentage based rgb values
Votemike
  • 762
  • 1
  • 10
  • 28
1
/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*((0+(?:\.\d+)?)|(1+(?:\.0+)?)))?\)$/.test('rgba(255,255,255,1.000000)')
Solirpa
  • 11
  • 1
1

It's impossible to write a regex that does everything you want, exactly. The core difficulty is that on the one hand, you want to capture the color values, yet on the other hand, whether there are commas between G, B and A depends on whether there was a comma between R and G. The only way to have that conditionality is to have a conditional capture group for the G value (and B and A). But you only want a result value that looks like ["rgb(1,2,3)", "1", "2", "3"]. The conditionality means that, if you want an expression that properly parses the syntax rules, you're going to get a result like ["rgb(1,2,3)", null, null, null, null, "1", "2", "3", null]. I don't see any way around that.

That said, there is an easy way to handle that slight messiness with a quick post-regex filter. With the right regex, all that is needed is to remove all null's from the result, which can be done by applying a filter() to the result array. Also, non-capturing groups, /(?:...)/, are your friend here - they let us group things to create the alternatives without adding captures to the result array.

I've based my rules for color and opacity values on the CSS documentation:

So, here's my best shot:

var testStrings = [
    // These should succeed:
    "rgb(0, 70, 255)",
    "rgba(0, 70, 127, 0.5)",
    "rgba(0, 70, 255, .555)",
    "rgba(0, 70, 255, 1.0)",
    "rgba(0, 70, 255, 5%)",
    "rgba(0, 70, 255, 40%)",
    "rgba(0, 70, 255, 0)",
    "rgba(0 70 255)",
    "rgba(0 70 25e1)",
    "rgb( 0 70 255 / 50 )",
    // These should fail:
    "rgb(0, 70 255)",
    "rgb(0 70 255, .5)",
    "rgb(0, 70 255)",
    "rgb(2.5.5)",
    "rgb(100, 100, 100 / 30)",
];

// It's easiest if we build up the regex string from some meaningful substrings.
// "comma" supports arbitrary space around a comma.
var comma = "\\s*,\\s*";
// "number" supports signed/unsigned integers, decimals,
// scientific notation, and percentages.
var number = "([+-]?\\d*.?\\d+(?:e[+-]?\\d+)?%?)";
// "commaList" matches 3- or 4-tuples like "0, 70, 255, 50".
var commaList = number + comma + number + comma + number +
                    "(?:" + comma + number + ")?";
// "noCommaList" supports "space-separated" format, "0 70 255 / 50".
var noCommaList = number + "\\s+" + number + "\\s+" + number +
                      "(?:\\s*/\\s*" + number + ")?";

// Here's our regex string - it's just the "rgb()" syntax
// wrapped around either a comma-separated or space-separated list.
var rgb = "^rgba?\\(\\s*(?:" + commaList + "|" + noCommaList + ")\\s*\\)$";

// Finally, we create the RegExp object.
var rgbRegex = new RegExp(rgb);

// Run some tests.
var results = testStrings.map(s => s.match(rgbRegex));

// Post-process to remove nulls
results = results.map(result => result && result.filter(s => s));

// Log the full regex string and all our test results.
console.log(rgbRegex);
console.log(results.map(result => JSON.stringify(result)).join('\n'));
Kevin Perry
  • 541
  • 3
  • 6
0

Pistus' answer seemed to be the closest for me to get working properly. The problem is you cannot attempt to run the same matching group twice using {2}, it just gives you the same answer twice of the second matching group.

This will handle the following sloppy CSS:

rgba(255 , 255 , 255 , 0.1 )
rgba( 255 , 255, 255 , .5 )
rgba( 0, 255 , 255 , 1.0 )
rgb( 0, 255 , 255 )
rgba( 0, 255 , 255 ) // <-- yes, not correct, but works

and give you a good, clean response. I didn't really need something that properly validated as much as I needed something that would give me clean numbers.

In the end, this is what worked for me:

rgba?\(\s*(25[0-5]|2[0-4]\d|1\d{1,2}|\d\d?)\s*,\s*(25[0-5]|2[0-4]\d|1\d{1,2}|\d\d?)\s*,\s*(25[0-5]|2[0-4]\d|1\d{1,2}|\d\d?)\s*,?\s*([01\.]\.?\d?)?\s*\)

Link to Regex101: https://regex101.com/r/brjTFf/1

Also, I cleaned up the matching groups so all you get is the cleanest JS array:

let regex = /rgba?\(\s*(25[0-5]|2[0-4]\d|1\d{1,2}|\d\d?)\s*,\s*(25[0-5]|2[0-4]\d|1\d{1,2}|\d\d?)\s*,\s*(25[0-5]|2[0-4]\d|1\d{1,2}|\d\d?)\s*,?\s*([01\.]\.?\d?)?\s*\)/;
let bgColor = "rgba(0, 255, 0, .5)";

console.log( bgColor.match( regex ) );

Here's the output from the console:

[
    "rgba(0, 255, 0, .5)",
    "0",
    "255",
    "0",
    ".5"
]
WebTigers
  • 297
  • 2
  • 8
0

You can concat 2 patterns by |, but then you have to handle 7 groups instead of 4.

^rgb(?:\((\d+),\s*(\d+),\s*(\d+)\)|a\((\d+),\s*(\d+),\s*(\d+),\s*(1|0?\.\d+)\))$

This would look like: concatination

(Image by jex.im)

It's not really complete, but should answere the question.

For test and debug:


To handle the goups, you can work with substituion: $1$4-$2$5-$3$6-$7.

So you will get

in:  rbg(1,2,3)
out: 1-2-3-

in:  rgba(1,2,3,0.3)
out: 1-2-3-0.3

then you can split.

Andy A.
  • 1,392
  • 5
  • 15
  • 28
0
rgba?\((\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,?\s*(\d{1,3})\s*(,\s*\d*\.\d\s*)?|\s*(\d{1,3})\s*(\d{1,3})\s*(\d{1,3})\s*(/?\s*\d+%)?(/\s*\d+\.\d\s*)?)\)

enter image description here

小弟调调
  • 1,315
  • 1
  • 17
  • 33
-1

Another version to support rgb and rgba separately , with not strict spaces and allowing pattern rgba(255,255,255, .5)

(rgb\((((25[0-5]|2[0-4]\d|1\d{1,2}|\d\d?)\s*?,\s*?){2}(25[0-5]|2[0-4]\d|1\d{1,2}|\d\d?)\s*?)?\))|(rgba\(((25[0-5]|2[0-4]\d|1\d{1,2}|\d\d?)\s*?,\s*?){2}(25[0-5]|2[0-4]\d|1\d{1,2}|\d\d?)\s*?,\s*(0?\.\d*|0(\.\d*)?|1)?\))
Arman Petrosyan
  • 867
  • 13
  • 26
-1

You can use that: 100% of capturing rgba or rgb color code

^(rgba(\((\s+)?(([0-9])|([1-9][0-9])|([1][0-9][0-9])|([2][0-5][0-5]))(\s+)?,(\s+)?(([0-9])|([1-9][0-9])|([1][0-9][0-9])|([2][0-5][0-5]))(\s+)?,(\s+)?(([0-9])|([1-9][0-9])|([1][0-9][0-9])|([2][0-5][0-5]))(\s+)?,(\s+)?((0|(0.[0-9][0-9]?)|1)|(1?[0-9][0-9]?\%))(\s+)?\)))|rgb(\((\s+)?(([0-9])|([1-9][0-9])|([1][0-9][0-9])|([2][0-5][0-5]))(\s+)?,(\s+)?(([0-9])|([1-9][0-9])|([1][0-9][0-9])|([2][0-5][0-5]))(\s+)?,(\s+)?(([0-9])|([1-9][0-9])|([1][0-9][0-9])|([2][0-5][0-5]))(\s+)?\))$