Blog

Validating IPv4 Addresses in Java: The Regex That Actually Works

Validating an IPv4 address sounds trivial until you realise that "looks like four numbers" is not the same as "is a valid IP." Leading zeros, octets above 255, and missing segments all break naïve checks.

According to codingnconcepts, the cleanest way to handle the validation in Java is a regex that enforces three rules at once: each segment stays between zero and 255, leading zeros are not allowed, and exactly four dot-separated segments must be present.

The short version:

  • An IPv4 address has four segments separated by dots.
  • Each segment must be a number between 0 and 255.
  • Leading zeros are not allowed.
  • A single regex enforces all three rules at once.
  • A shorter version factors the common digit pattern out of the alternation.
  • Java's String.matches method is enough for a basic check.

The format and the regex

A valid IPv4 address looks like 192.168.0.1 or 255.255.255.255. Each segment is one to three digits, with values from 0 to 255. The trick is rejecting leading zeros, because 01 in 192.168.0.01 is technically a string but not a valid octet.

The pattern that handles all of this looks like this: four copies of a group that matches a valid octet, an optional dot, and a word boundary. The octet alternation is the heart of it.

Breaking it down:

  • d matches single digits, 0 through 9.
  • [1-9]d matches the two-digit range starting at 10.
  • 1dd matches the three-digit range starting at 100.
  • 2[0-4]d matches the range starting at 200 and ending just below 250.
  • 25[0-5] matches the band from 250 to 255 inclusive.
  • Together they cover the full 0 to 255 range without leading zeros.
  • .?b matches an optional dot followed by a word boundary.
  • {4} repeats the pattern exactly four times.

A more concise version factors the common d out of the alternation so the whole pattern fits on one line of logic. Both versions reject the same inputs. The concise version is easier to scan but slightly harder to read. Pick based on your team's style.

The Java method

A straightforward validation method is small enough to live in a utility class. In Java, the backslashes need to be escaped, which is why d becomes \d in the source. That is the only difference from the standalone regex. The implementation reads almost like the pattern itself.

The method takes a string, holds the pattern as a constant, and returns the result of String.matches. Two lines of logic, plus the method signature. Wrap the pattern, hand it the user input, and the matcher does the rest.

Tests that prove it works

Valid inputs the method should accept:

  • 0.0.0.0
  • 127.0.0.1
  • 192.168.10.1
  • 172.16.254.1
  • 192.168.1.255
  • 255.255.255.255
  • 10.98.30.56

Invalid inputs the method should reject:

  • 192.168.0.256 (segment above 255)
  • 192.168.0.01 (leading zero)
  • 192.168.0 (only three segments)
  • .192.168.0.1 (leading dot)
  • .192.168.0.1. (trailing dot)

If your implementation accepts all of those, the regex is doing its job.

Our analysis: regex versus parser

For most validation tasks, this regex is enough. It is short, well understood, and runs in linear time on reasonable input. The honest limit is that regex cannot tell you whether an address is routable, private or reserved. For that, you need a real parser or a library.

The leading-zero rule is the most common edge case in real code. Many implementations accept "01" as a valid octet because they only check that each segment is a number, not that the number respects standard IP notation. That bug is invisible until someone feeds a user-typed string into your database. The pattern above closes that hole cleanly.

The other lesson is test coverage. The habit of running both a positive and a negative suite against the validation method catches the edge cases that break naïve implementations. Five positive cases and five negative cases is enough to catch most regressions. Skip the negative tests and you will eventually ship a bug.

One thing to keep in mind: regex is great for shape, weak for meaning. Once you need to know whether 10.0.0.0/8 is a private range, regex is the wrong tool. Reach for InetAddress from java.net or a CIDR parser.

Worked example: Say you are validating a user-supplied IP

A user types "192.168.0.01" into a settings form. Your naïve parser splits on dots, parses each segment as an integer and accepts it as "192.168.0.1." Your regex-based parser rejects it as invalid, because of the leading zero. The user sees a friendly error and corrects the input.

Now imagine the same user types a string where one segment is a sequence of digits that parses as an integer far above 0. Naïve parsing might accept it because each segment parses as an integer. The regex rejects it because that segment is outside the 0 to 255 range.

A short method that calls String.matches is easier to reason about than a hand-rolled loop, and the tests above document the behaviour right next to the code.

What to watch next

Watch IPv6 creeping into the same validation paths. The regex above only handles IPv4. If your application accepts both, you need a separate branch that handles the colon-separated, hexadecimal IPv6 format. For most internal tooling that only ever sees IPv4, this regex is sufficient.

Key takeaways

  • The regex enforces value range, segment count and leading-zero rules in one pass.
  • A five-line Java method is enough to wrap it.
  • Test with both valid and invalid inputs to catch the obvious edge cases.
  • For semantic checks like private ranges, use a real parser instead of regex.

Frequently asked questions

Does the pattern accept IPv6 addresses?

No, this regex only validates IPv4. IPv6 uses a different format and needs a separate pattern.

Why are leading zeros rejected?

They are not standard IPv4 notation. 01 is technically a valid integer but not a valid IP octet.

Can the regex tell me if an address is private or public?

No, it only checks the format. You need a separate check for reserved ranges.

Is regex fast enough for high-volume validation?

Yes, the pattern runs in linear time on typical input sizes.

Related