With much help from @nhahtdh in this answer https://stackoverflow.com/a/21943960/3112803 I have found breaking it up to be the best solution. Below is an example of how to do it in PL/SQL
, but it could be done this way in other languages. I'll do the same in ColdFusion
. For PL/SQL
the pattern needed to stay under 512 characters so breaking it up works great and it is simple to understand. It passed all my test cases in the original question.
if (
/* IPv6 expanded */
REGEXP_LIKE(v, '\A[[:xdigit:]]{1,4}(:[[:xdigit:]]{1,4}){7}\z')
/* IPv6 shorthand */
OR (NOT REGEXP_LIKE(v, '\A(.*?[[:xdigit:]](:|\z)){8}')
AND REGEXP_LIKE(v, '\A([[:xdigit:]]{1,4}(:[[:xdigit:]]{1,4}){0,6})?::([[:xdigit:]]{1,4}(:[[:xdigit:]]{1,4}){0,6})?\z'))
/* IPv6 dotted-quad notation, expanded */
OR REGEXP_LIKE(v, '\A[[:xdigit:]]{1,4}(:[[:xdigit:]]{1,4}){5}:(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}\z')
/* IPv6 dotted-quad notation, shorthand */
OR (NOT REGEXP_LIKE(v, '\A(.*?[[:xdigit:]]:){6}')
AND REGEXP_LIKE(v, '\A([[:xdigit:]]{1,4}(:[[:xdigit:]]{1,4}){0,4})?::([[:xdigit:]]{1,4}:){0,5}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}\z'))
) then