How can I match IPv6 addresses with a Perl regex?

10,750

Solution 1

This contains a patch to Regexp::Common demonstrating a complete, accurate, tested IPv6 regex. Its a straight translation of the IPv6 grammar. Regexp::IPv6 is also accurate.

More importantly, it contains a test suite. Running it with your regex shows you're still a ways off. 10 out of 19 missed. 1 out of 12 false positives. IPv6 contains a lot of special shorthands making it very easy to get subtly wrong.

Best place to read up on what goes into an IPv6 address is RFC 3986 section 3.2.2.

Solution 2

What do you mean you can't just use a library? How about a module? Regexp::IPv6 will give you what you need.

Solution 3

I'm not an IPv6 expert, but please trust me when I tell you that matching (let alone validating) IPv6 addresses is not easy with a very simple regex such as the one you suggest. There's many shorthands and various conventions for combining the address with a port, just to name an example. One such shorthand is that you can write 0:0:0:0:0:0:0:1 as ::1, but there's more. If you read German, I would suggest looking at the slides of Steffen Ullrich's talk at the 11th German Perl Workshop.

You say you can't use a library, but if you're going to reinvent the whole complexity of the library, then you could as well just import it verbatim into your project.

Solution 4

This mostly works...

^([0-9a-fA-F]{0,4}|0)(\:([0-9a-fA-F]{0,4}|0)){7}$

Cons: :: like cases not handled correctly

Solution 5

Try this:

^([0-9a-fA-F]{4}|0)(\:([0-9a-fA-F]{4}|0)){7}$

From Regular Expression Library: IPv6 address

You should also read this: A Regular Expression for IPv6 Addresses

Share:
10,750
imnotneo
Author by

imnotneo

Updated on June 04, 2022

Comments

  • imnotneo
    imnotneo almost 2 years

    So I need to match an ipv6 address which may or may not have a mask. Unfortunately I can't just use a library to parse the string.

    The mask bit is easy enough, in this case:

    (?:\/\d{1,3})?$/
    

    The hard part is the different formats of an ipv6 address. It needs to match ::beef, beef::, beef::beef, etc.

    An update: I'm almost there..

    /^(\:\:([a-f0-9]{1,4}\:){0,6}?[a-f0-9]{0,4}|[a-f0-9]{1,4}(\:[a-f0-9]{1,4}){0,6}?\:\:|[a-f0-9]{1,4}(\:[a-f0-9]{1,4}){1,6}?\:\:([a-f0-9]{1,4}\:){1,6}?[a-f0-9]{1,4})(\/\d{1,3})?$/i
    

    I am, in this case restricted to using perl's regex.