Hemaulo
Hemaulo

Reputation: 999

Extract all hex colors from a multiline CSS string

I'm trying to write regex that extracts all hex colors from CSS code.

This is what I have now:

Code:

$css = <<<CSS

/* Do not match me: #abcdefgh; I am longer than needed. */

.foo
{
    color: #cccaaa; background-color:#ababab;
}

#bar
{
    background-color:#123456
}
CSS;

preg_match_all('/#(?:[0-9a-fA-F]{6})/', $css, $matches);

Output:

Array
(
    [0] => Array
        (
            [0] => #abcdef
            [1] => #cccaaa
            [2] => #ababab
            [3] => #123456
        )

)

I don't know how to specify that only those colors are matched which ends with punctuation, whitespace or newline.

Upvotes: 40

Views: 36077

Answers (5)

The accepted answer shows you how to extract hexcodes with regex, because that is the asked question.

If you, instead, want to validate if an isolated string (without preceeding #) IS a hexcode, you can use two non-regex functions.

if (ctype_xdigit($color) && strlen($color)==6) {
    // yay, it's a hex color!
}

Upvotes: 44

GolezTrol
GolezTrol

Reputation: 116110

Since a hex color code may also consist of 3 characters, you can define a mandatory group and an optional group of letters and digits, so the long and elaborate notation would be:

/#([a-f]|[A-F]|[0-9]){3}(([a-f]|[A-F]|[0-9]){3})?\b/

Or if you want a nice and short version, you can say that you want either 1 or 2 groups of 3 alphanumeric characters, and that they should be matched case insensitively (/i).

/#([a-f0-9]{3}){1,2}\b/i

Instead of [a-f0-9] you can also write [[:xdigit:]], if the regex engine supports this posix character class. In this case you can skip the /i at the end, and the whole formula is only two characters more, but arguably more descriptive.

/#([[:xdigit:]]{3}){1,2}\b/

Upvotes: 63

Berry M.
Berry M.

Reputation: 489

Despite this question's age I'd like to ammend the following:

^#([[:xdigit:]]{3}){1,2}$, where [[:xdigit:]] is a shorthand for [a-fA-F0-9].

So:
<?php preg_match_all("/^#(?>[[:xdigit:]]{3}){1,2}$/", $css, $matches) ?>

Also noteworthy here is the usage of a non-capturing group (?>...), to ensure we don't store data in memory we never wanted to store in the first place.

Try it online

Upvotes: 2

robinst
robinst

Reputation: 31417

Shorter version of GolezTrol's answer that avoids writing the character set twice:

/#([a-fA-F0-9]{3}){1,2}\b/

Upvotes: 21

Elias Van Ootegem
Elias Van Ootegem

Reputation: 76395

I'm not entirely sure if I got this right, but if you only want to match hex colors at the end of a CSS line:

preg_match_all('/#(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})[\s;]*\n/',$css,$matches);

should work, all I did was add the optional \s; char group (optional semi-colon and spaces) and a line-break character (not optional) and it seemed to work.
And as @GolezTrol pointed out #FFF; is valid, too.

When tested on this:

$css = '/* Do not match me: #abcdefgh; I am longer than needed. */
.foo
{
    color: #CAB;
    background-color:#ababab;
}';
preg_match_all('/#(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})[\s;]*\n/',$css,$matches);
var_dump($matches);

The output was:

array (array('#CAB;','#ababab;'))

Upvotes: 1

Related Questions