A clear explanation of validating IPv4 and IPv6 addresses by checking segment count, length, characters, range, and leading-zero rules.
Problem Restatement
We are given a string queryIP.
We need to return one of three strings:
"IPv4"
"IPv6"
"Neither"Return "IPv4" if the string is a valid IPv4 address.
Return "IPv6" if the string is a valid IPv6 address.
Otherwise, return "Neither".
A valid IPv4 address has four decimal parts separated by dots. Each part must be between 0 and 255, and leading zeros are not allowed. A valid IPv6 address has eight hexadecimal parts separated by colons. Each part must have length from 1 to 4, and leading zeros are allowed. The official problem asks us to classify queryIP as "IPv4", "IPv6", or "Neither".
Input and Output
| Item | Meaning |
|---|---|
| Input | A string queryIP |
| Output | "IPv4", "IPv6", or "Neither" |
| IPv4 separator | Dot . |
| IPv6 separator | Colon : |
| IPv4 parts | Exactly 4 |
| IPv6 parts | Exactly 8 |
Function shape:
def validIPAddress(queryIP: str) -> str:
...Examples
Example 1:
queryIP = "172.16.254.1"It has four dot-separated parts:
172
16
254
1Each part is numeric, each value is between 0 and 255, and there are no leading zeros.
Answer:
"IPv4"Example 2:
queryIP = "2001:0db8:85a3:0:0:8A2E:0370:7334"It has eight colon-separated parts.
Each part has length from 1 to 4.
Each character is a valid hexadecimal character.
Answer:
"IPv6"Example 3:
queryIP = "256.256.256.256"Each part is numeric, but 256 is larger than 255.
Answer:
"Neither"Example 4:
queryIP = "192.168.01.1"The part "01" has a leading zero.
Answer:
"Neither"First Thought: Use Built-In IP Parsing
In real application code, we might use a standard library to parse IP addresses.
But in this problem, we need to implement the validation rules directly.
Also, the problem has exact rules:
| Type | Rule |
|---|---|
| IPv4 | Four decimal parts |
| IPv4 | No leading zeros unless the part is exactly "0" |
| IPv4 | Each value from 0 to 255 |
| IPv6 | Eight hexadecimal parts |
| IPv6 | Each part has length 1 to 4 |
| IPv6 | Leading zeros are allowed |
So the clean approach is to write two helpers:
is_ipv4(queryIP)
is_ipv6(queryIP)Then return the matching type.
Key Insight
IPv4 and IPv6 have different separators.
An IPv4 candidate uses dots:
.An IPv6 candidate uses colons:
:So we can check based on which separator appears.
But we still must validate every segment carefully.
For IPv4:
"172.16.254.1"splits into:
["172", "16", "254", "1"]For IPv6:
"2001:0db8:85a3:0:0:8A2E:0370:7334"splits into:
["2001", "0db8", "85a3", "0", "0", "8A2E", "0370", "7334"]The problem becomes segment validation.
IPv4 Validation Rules
A valid IPv4 address must satisfy all of these:
| Rule | Example valid | Example invalid |
|---|---|---|
| Exactly 4 parts | "1.2.3.4" | "1.2.3" |
| Every part is non-empty | "1.2.3.4" | "1..3.4" |
| Every part contains only digits | "172" | "17a" |
| No leading zero if length > 1 | "0", "10" | "01" |
| Value is between 0 and 255 | "255" | "256" |
IPv6 Validation Rules
A valid IPv6 address must satisfy all of these:
| Rule | Example valid | Example invalid |
|---|---|---|
| Exactly 8 parts | Eight groups | Seven groups |
| Every part length is 1 to 4 | "0", "0db8" | "", "02001" |
| Characters are hexadecimal | "8A2E" | "037j" |
| Leading zeros are allowed | "0000" | Not invalid |
The valid hexadecimal characters are:
0123456789abcdefABCDEFAlgorithm
Define is_ipv4(ip).
- Split by
".". - If there are not exactly
4parts, returnFalse. - For each part:
- If it is empty, return
False. - If it contains a non-digit character, return
False. - If it has length greater than
1and starts with"0", returnFalse. - If its integer value is greater than
255, returnFalse.
- If it is empty, return
- Return
True.
Define is_ipv6(ip).
- Split by
":". - If there are not exactly
8parts, returnFalse. - For each part:
- If its length is less than
1or greater than4, returnFalse. - If any character is not hexadecimal, return
False.
- If its length is less than
- Return
True.
Then:
- If
is_ipv4(queryIP), return"IPv4". - If
is_ipv6(queryIP), return"IPv6". - Return
"Neither".
Correctness
The IPv4 helper checks exactly the IPv4 rules.
It requires exactly four parts, rejects empty parts, rejects non-digit characters, rejects leading zeros, and checks that each numeric value lies between 0 and 255.
Therefore, it returns True exactly for valid IPv4 addresses.
The IPv6 helper checks exactly the IPv6 rules.
It requires exactly eight parts, checks that every part has length from 1 to 4, and verifies that every character belongs to the hexadecimal character set.
Therefore, it returns True exactly for valid IPv6 addresses.
The main function returns "IPv4" if and only if the IPv4 helper accepts the input, "IPv6" if and only if the IPv6 helper accepts it, and "Neither" otherwise.
So the algorithm returns the required classification.
Complexity
Let n = len(queryIP).
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | Each character is checked a constant number of times |
| Space | O(n) | Splitting creates segment lists |
The number of segments is bounded, but the split strings still depend on the input length.
Implementation
class Solution:
def validIPAddress(self, queryIP: str) -> str:
def is_ipv4(ip: str) -> bool:
parts = ip.split(".")
if len(parts) != 4:
return False
for part in parts:
if part == "":
return False
if not part.isdigit():
return False
if len(part) > 1 and part[0] == "0":
return False
if int(part) > 255:
return False
return True
def is_ipv6(ip: str) -> bool:
parts = ip.split(":")
if len(parts) != 8:
return False
valid = set("0123456789abcdefABCDEF")
for part in parts:
if len(part) < 1 or len(part) > 4:
return False
for ch in part:
if ch not in valid:
return False
return True
if is_ipv4(queryIP):
return "IPv4"
if is_ipv6(queryIP):
return "IPv6"
return "Neither"Code Explanation
The IPv4 checker starts by splitting on dots:
parts = ip.split(".")A valid IPv4 address must have exactly four parts:
if len(parts) != 4:
return FalseThen each part is validated.
Empty parts are invalid:
if part == "":
return FalseNon-digit parts are invalid:
if not part.isdigit():
return FalseLeading zeros are invalid when the part has more than one character:
if len(part) > 1 and part[0] == "0":
return FalseThe numeric range must be at most 255:
if int(part) > 255:
return FalseThe IPv6 checker splits on colons:
parts = ip.split(":")A valid IPv6 address must have exactly eight parts:
if len(parts) != 8:
return FalseEach part must have length from 1 to 4:
if len(part) < 1 or len(part) > 4:
return FalseEvery character must be hexadecimal:
if ch not in valid:
return FalseThe main function checks IPv4 first, then IPv6:
if is_ipv4(queryIP):
return "IPv4"
if is_ipv6(queryIP):
return "IPv6"
return "Neither"Alternative Implementation With Separator Choice
We can avoid running both validators in many cases by checking the separator first.
class Solution:
def validIPAddress(self, queryIP: str) -> str:
if "." in queryIP:
return "IPv4" if self._is_ipv4(queryIP) else "Neither"
if ":" in queryIP:
return "IPv6" if self._is_ipv6(queryIP) else "Neither"
return "Neither"
def _is_ipv4(self, ip: str) -> bool:
parts = ip.split(".")
if len(parts) != 4:
return False
for part in parts:
if part == "":
return False
if not part.isdigit():
return False
if len(part) > 1 and part[0] == "0":
return False
if int(part) > 255:
return False
return True
def _is_ipv6(self, ip: str) -> bool:
parts = ip.split(":")
if len(parts) != 8:
return False
valid = set("0123456789abcdefABCDEF")
for part in parts:
if not 1 <= len(part) <= 4:
return False
for ch in part:
if ch not in valid:
return False
return TrueThe first implementation is compact and works well.
Testing
def run_tests():
s = Solution()
assert s.validIPAddress("172.16.254.1") == "IPv4"
assert s.validIPAddress("2001:0db8:85a3:0:0:8A2E:0370:7334") == "IPv6"
assert s.validIPAddress("256.256.256.256") == "Neither"
assert s.validIPAddress("192.168.01.1") == "Neither"
assert s.validIPAddress("192.168.1.00") == "Neither"
assert s.validIPAddress("[email protected]") == "Neither"
assert s.validIPAddress("2001:0db8:85a3::8A2E:037j:7334") == "Neither"
assert s.validIPAddress("02001:0db8:85a3:0000:0000:8a2e:0370:7334") == "Neither"
assert s.validIPAddress("1.1.1.1.") == "Neither"
assert s.validIPAddress("1..1.1") == "Neither"
assert s.validIPAddress("0.0.0.0") == "IPv4"
assert s.validIPAddress("255.255.255.255") == "IPv4"
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
"172.16.254.1" | Valid IPv4 |
| Valid long IPv6 | Valid IPv6 with mixed case |
"256.256.256.256" | IPv4 parts out of range |
"192.168.01.1" | IPv4 leading zero |
"192.168.1.00" | IPv4 leading zero |
"[email protected]" | Invalid IPv4 character |
IPv6 with :: | Empty IPv6 group is invalid here |
IPv6 group length 5 | IPv6 group too long |
| Trailing dot | Empty IPv4 group |
| Double dot | Empty IPv4 group |
"0.0.0.0" | Smallest IPv4 parts |
"255.255.255.255" | Largest IPv4 parts |