Best way to determine a string is a valid color hexcode

Let’s say you have a Hexcode as a string [Ex: “AB124E”]

My initial thought was to put the string through formatting with the %x character class, like so:

local checkHex = string.format("%X",Color)

This actually is looking for Color to be decimal numbers to convert to Hex.

My next thought was to go through each character, looking only for:
“0123456789ABCDEF” [valid hex characters]
and replace everything else with nothing.

Then after that, check if the string is still 6 characters. If it is 6 characters, the it should be a valid hex code. While I think this idea is fine, and works, I was curious to see if there’s a better way I just have been overlooking.


You could use string.find and string.sub to check for the Valid Hex Characters and then with string.sub remove the rest.

I think this would work, there is no other way to find out if a string is HexCode. Also just so you know, hexcode can be 3 characters too so you should keep every code that has more than 2 and less than 7

function checkHex(HexCode)
  local FilteredHexCode = string.match(HexCode, "%w+")  -- will only keep letters and numbers

  if #FilteredHexCode > 6 or #FilteredHexCode < 3 then
	return false
    return true

print(checkHex("ABCDEF")) -- would return true
print(checkHex("%GHIJKLMN")) -- would return false due having more than 6 characters (it will get rid of the special character)

Simply checking the size of it is not checking if it’s Hex, your version would accept Z as a valid Hex Letter, when it is not. Only A-F is. And while I’m familiar with HexColorCodes that are only 3 characters instead of 6, for my purpose I will instruct users to use 6 character hex codes.

local Hex = "AB124E"
local success, result = pcall(function()
	return Color3.fromHex(Hex)

I thought about tossing it into fromHex, but figured it’d accept wrong answers and just output a white color. Should’ve just tried it in case. :man_facepalming:

You can use string.match which uses string patterns and regular expressions to return the first match in some string. For your case, we want to match any hexcode string, which you’ve defined is a 6-character length string of hexadecimal characters.

function checkHex(str)
   return string.match(str, "^%x%x%x%x%x%x$") --This pattern explained below
checkHex("AB124E") --> "AB144E"
checkHex("FF00FF1") --> nil   (the string was too long)
checkHex("12345") --> nil   (the string wasn't long enough)
checkHex("Q12345") --> nil   (the string had a non-hexadecimal character)

The ‘pattern’ which the string.match function uses to compare here has a few components to it:

  • The’%x’ is used 6 times (once for each hexadecimal digit) and matches any valid hexadecimal character [0-9, A-F, a-f]
  • The ‘^’ indicates the beginning of the string
  • The ‘$’ indicates the end of the string

If the ‘^’ and ‘$’ characters were omitted, then there would be the potential to match strings a lot longer than a valid 6-character hexcode. For example:

   "Not a hexcode1234. But this pattern will still match it.",
)) --> de1234

The tonumber function can also convert hexadecimal.

local hex = "FFFFFF"
local dec = tonumber(hex, 16)
assert(dec ~= nil, "Invalid Hex Code")
local r = bit32.extract(dec, 16, 8)
local g = bit32.extract(dec, 8, 8)
local b = bit32.extract(dec, 0, 8)
local color = Color3.fromRGB(r, g, b)
local function checkHex(s)
	return (s:len() == 6) and (s == s:format("%x", s))

print(checkHex("aaaaaa")) --true

This also avoids pcall(), making it more performant.