-1

I'm using the current code, suggested here on SO, to get the RGB values from a string like "rgb(0, 0, 0)" but also it can be "rgb(0, 0, 0,096)", so now i need to get also the alpha value

function getRGB(str) {
    var match = str.match(/rgba?\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)?(?:, ?(\d(?:\.\d?))\))?/);
    arr = [match[1], match[2], match[3]];
    return arr;
}

I tried this code below but it doesn't work

function getRGBA(str) {
    var match = str.match(/rgba?\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)?(?:, ?(\d(?:\.\d?))\))?/);
    arr = [match[1], match[2], match[3], match[4]];
    return arr;
}
Francesco
  • 24,839
  • 29
  • 105
  • 152

1 Answers1

2

Your original regex is already allowing for the alpha (there are four sets of digits with commas between, where the fourth is optional; also note that the a in rgba near the beginning is optional because of the ? after it). You can tell whether you got the alpha by looking at match[4]:

function getRGB(str) {
    var match = str.match(/rgba?\((\d{1,3}), ?(\d{1,3}), ?(\d{1,3})\)?(?:, ?(\d(?:\.\d?))\))?/);
    arr = [match[1], match[2], match[3]];
    if (match[4]) {          // ***
        arr.push(match[4]);  // ***
    }                        // ***
    return arr;
}

Not strictly what you asked, but that regular expression has a few issues that will prevent it from reliably detecting strings:

  • It only allows one space after commas, but more than one space is valid
  • It doesn't allow for any spaces before commas
  • In the alpha part, it allows for 1. but that's invalid without a digit after the .

A couple of other notes:

  • That code relies on what I call The Horror of Implicit Globals because it never declares arr.
  • The code isn't converting the values to numbers. I don't know if you wanted to do that or not, but I figured it was worth noting.
  • The function throws an error if the string doesn't match the expression. Maybe that's what you want, but again I thought I'd flag it up.

This expression does a better job of matching:

/rgba?\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:\)|,\s*(\d?(?:\.\d+)?)\))/

Live Example:

function num(str) {
    str = str.trim();
    // Just using + would treat "" as 0
    // Using parseFloat would ignore trailing invalid chars
    // More: https://stackoverflow.com/questions/28994839/why-does-string-to-number-comparison-work-in-javascript/28994875#28994875
    return str === "" ? NaN : +str;
}

function getRGB(str) {
    const match = str.match(/rgba?\((\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:\)|,\s*(\d?(?:\.\d+)?)\))/);
    if (!match) {
        return null;
    }
    const arr = [
        num(match[1]),
        num(match[2]),
        num(match[3])
    ];
    if (match[4]) {
        arr.push(num(match[4]));
    }
    return arr;
}

function test(str) {
    console.log(str, "=>", JSON.stringify(getRGB(str)))
}

test("rgb(1,2,3,4)");       // [1, 2, 3, 4]
test("rgba(1 , 2, 3, 4)");       // [1, 2, 3, 4]
test("rgba(111   , 22, 33)");    // [111, 22, 33]
test("rgb(111, 22)");           // null (doesn't match)
test("rgb(111, 22   , 33, 1)"); // [111, 22, 33, 1]
test("rgb(111, 22, 33, 1.)");   // null (doesn't match, no digit after .)
test("rgb(111, 22, 33, 1.0)");  // [111, 22, 33, 1]
test("rgb(111, 22, 33, .5)");   // [111, 22, 33, 0.5]
.as-console-wrapper {
    max-height: 100% !important;
}

And in modern JavaScript environments we could make it a bit simpler to use by using named capture groups (see the live example for the num function and why I use it rather than +/Number or parseFloat):

function getRGB(str) {
    const {groups} = str.match(
        /rgba?\((?<r>\d{1,3})\s*,\s*(?<g>\d{1,3})\s*,\s*(?<b>\d{1,3})\s*(?:\)|,\s*(?<a>\d?(?:\.\d+)?)\))/
    ) ?? {groups: null};
    if (!groups) {
        return null;
    }
    const arr = [
        num(groups.r),
        num(groups.g),
        num(groups.b)
    ];
    if (groups.a) {
        arr.push(num(groups.a));
    }
    return arr;
}

Live Example:

function num(str) {
    str = str.trim();
    // Just using + would treat "" as 0
    // Using parseFloat would ignore trailing invalid chars
    // More: https://stackoverflow.com/questions/28994839/why-does-string-to-number-comparison-work-in-javascript/28994875#28994875
    return str === "" ? NaN : +str;
}

function getRGB(str) {
    const {groups} = str.match(
        /rgba?\((?<r>\d{1,3})\s*,\s*(?<g>\d{1,3})\s*,\s*(?<b>\d{1,3})\s*(?:\)|,\s*(?<a>\d?(?:\.\d+)?)\))/
    ) ?? {groups: null};
    if (!groups) {
        return null;
    }
    const arr = [
        num(groups.r),
        num(groups.g),
        num(groups.b)
    ];
    if (groups.a) {
        arr.push(num(groups.a));
    }
    return arr;
}

function test(str) {
    console.log(str, "=>", JSON.stringify(getRGB(str)))
}

test("rgb(1,2,3,4)");       // [1, 2, 3, 4]
test("rgba(1 , 2, 3, 4)");       // [1, 2, 3, 4]
test("rgba(111   , 22, 33)");    // [111, 22, 33]
test("rgb(111, 22)");           // null (doesn't match)
test("rgb(111, 22   , 33, 1)"); // [111, 22, 33, 1]
test("rgb(111, 22, 33, 1.)");   // null (doesn't match, no digit after .)
test("rgb(111, 22, 33, 1.0)");  // [111, 22, 33, 1]
test("rgb(111, 22, 33, .5)");   // [111, 22, 33, 0.5]
.as-console-wrapper {
    max-height: 100% !important;
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • This doesn't match the alpha string correctly, it only accounts for alphas >0 and <1 and they need to have the leading 0 (e.g. 0.2 would work). But alphas can be set as e.g. .33 or they can be 0 and 1. It should rather be something like `(\d?(?:\.\d+)?)` This one is not perfect either but at least you get your alpha. – Fygo Oct 15 '21 at 09:05
  • 1
    @Fygo - Thanks!! I was really puzzled by the regex because I thought I'd written it and it just didn't look like what I'd write -- then I realized, it's the OP's regex, not mine. I didn't really validate the expression when answering the question, I just pointed out how they could use the optional capture group at the end. But I should have, and I have now. Thanks again! – T.J. Crowder Oct 15 '21 at 09:56