IPv4格式
32位,4个字节的数字表示。
点分格式展示为0-255.0-255.0-255.0-255
因此,IPv4格式还比较好判断。
IPv4 java正则表达式判断
"^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$"
^表示行开始,$表示行结束,?表示0或者1个
(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d) //0-255
25[0-5]
如果是25开头的三位数,则后面只能跟着0-5
2[0-4]\\d
如果是2开头(非25X)的三位数,则后面可以跟着0-4,后面跟任意数字
[0-1]?\\d?\\d
不是以2开头的,则可能范围是0-199。百位:0和1选一个,或没有;十位:任意数字或者没有,个位:任意数字。
(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d))
前面加一个.,后面和前面的分析一样
{3}
前面括号里的内容重复三次
IPv6格式
128位,按每16位划分为一个段,将每个段转换成十六进制数字,并用冒号隔开。
IPv6压缩规则:
- 省略前导零
通过省略前导零指定 IPv6 地址。例如,IPv6 地址 1050:0000:0000:0000:0005:0600:300c:326b
可写为 1050:0:0:0:5:600:300c:326b
。
- 使用双冒号
通过使用双冒号(::
)代替一系列零来指定 IPv6 地址。例如,IPv6 地址 ff06:0:0:0:0:0:0:c3
可写为 ff06::c3
。一个 IP 地址中只可使用一次双冒号。
Ipv6中混合Ipv4
通过 IPv4 映射的 IPv6 地址
此类型的地址用于将 IPv4 节点表示为 IPv6 地址。它允许 IPv6 应用程序直接与 IPv4 应用程序通信。例如,0:0:0:0:0:ffff:192.1.56.10
和 ::ffff:192.1.56.10/96
(短格式)。
兼容 IPv4 的 IPv6 地址
此类型的地址用于隧道传送。它允许 IPv6 节点通过 IPv4 基础结构通信。例如,0:0:0:0:0:0:192.1.56.10
和 ::192.1.56.10/96
(短格式)。
Java 判断IP地址合法
下面的程序能判断Ip字符串是否是Ipv4地址,并能判断一部分情况是否是合法的ipv6地址
下面的程序来自参考博客,程序没有考虑IPv6中混合IPv4格式的情况,因此不够全面。
程序中以JDK中的方法IPAddressUtil.isIPv4LiteralAddress(ip)
来进行结果对比,但是JDK中的方法判断IP格式存在一些不全面的地方,例如判断IPv4格式时,输入“123”
也会返回true
,具体原因我之后再找时间分析一下源码。
private static final Pattern IPV4_REGEX =
Pattern.compile("^(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}$");
//无全0块,标准IPv6地址的正则表达式
private static final Pattern IPV6_STD_REGEX =
Pattern.compile("^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$");
//压缩正则表达式
private static final Pattern IPV6_COMPRESS_REGEX =
Pattern.compile("^(([0-9A-Fa-f]{1,4}(:[0-9A-Fa-f]{1,4})*)?)::((([0-9A-Fa-f]{1,4}:)*[0-9A-Fa-f]{1,4})?)$");
//判断是否为合法IPv4地址
public static boolean isIPv4Address(final String input) {
return IPV4_REGEX.matcher(input).matches();
}
//判断是否为合法IPv6地址
public static Map isIPv6Address(String input) {
Map resultMap = new HashMap();
// 8*4 位数字,加上七个: ipv6地址长度不可能超过39位
if (input.length() > 39) {
resultMap.put("value", "false");
resultMap.put("method", "长度非法");
} else if (IPV6_STD_REGEX.matcher(input).matches()) {
//标准格式判断
resultMap.put("value", "true");
resultMap.put("method", "标准格式");
} else if (IPV6_COMPRESS_REGEX.matcher(input).matches()) {
//压缩发生在IP地址内部;唯一确定内容,IP地址首尾无冒号 (:)
//压缩位置未知,压缩的连续全0块数量未知
resultMap.put("value", "true");
resultMap.put("method", "压缩格式");
} else {
resultMap.put("value", "false");
resultMap.put("method", "匹配错误");
}
return resultMap;
}
//测试数据
public static String[] getParams() {
return new String[]{
"::",
"::192.168.89.9",
"2001:4860:4860::8888",
"2400:da00::dbf:0:100",
"2404:6800:8005::d2",
"2A00:1450:8006::30",
"0:0:0:0:0:0:0:0",
"0:0:0:0:0:ffff:192.1.56.10",
"::192.1.56.10",
"::ffff:192.1.56.10/96",
"1030:0:0:0:C9B4:FF12:48AA:1A2B",
"0:0:0:0:0:0:192.1.56.10",
"::",
"a:b:c:D:e:f:f:F",
"a:b:c:D:G:f:f:F",
"a:b:c:D:g:f:f:F",
"fe80:1295:8030:49ec::1fc6:57fa:2222:0000",
"fe80:1295:8030:49ec:1fc6:57fa:0000:",
":1295:8030:49ec:1fc6:57fa:0000:0000",
"fe80:1295:8030:1fc6:57fa:0000:0000",
"fe80:1295:8030:49ec:1fc6:57fa::0000",
"fe80::0000",
"fe80::",
"::0000",
"fe80:1295:8030:0000",
"fe80:1295:8030::0000:0000",
"fe80:1295:8030:49ec:1fc6:57fa:::0000:0000:0000:0000:0000:0000:0000:0000",
"::8030:49ec:1fc6:57fa:0000:0000:0000:0000:0000:0000",
"fe80:1295::49ec:1fc6:57fa:0000:0000",
"fe80:1295:8030:49ec::1fc6:57fa:0000:0000:0000:0000:0000:0000:0000:0000:0000",
"fe80::1295:8030:49ec:1fc6:57fa:0000:0000:0000",
"fe80:1295:8030:49ec::1fc6:57fa:0000:0000:0000:0000:0000:0000:0000:0000:0000:0000:0000",
"fe80:1295:8030:49ec:1fc6::57fa:0000:0000:0000:0000:0000",
"fe80:1295:8030:49ec:1fc6:57fa::0000:0000:0000:0000:0000",
"fe80:1295:8030:49ec:1fc6:57fa:0000::0000:0000:0000:0000",
"fe80:0000:0000:0000:1fc6:57fa::1241:0000:0000:0000:0000:0000:0000",
"::fe80:1295:8030:49ec:1fc6:57fa:2222:0000",
":1295:8030:49ec:1fc6:57fa:2222:0000",
"fe80:1295:8030:49ec:1fc6:57fa:2222:0000:",
"fe80:1295:8030:49ec:1fc6:57fa:2222:0000::",
"::fe80:1295:8030:49ec:1fc6:57fa:2222:0000",
"fe80:1295:8030:49ec:1fc6:57fa:2222:",
":1295:8030:49ec:1fc6:57fa:2222:0000",
"fe80:1295:8030:49ec:1fc6:57fa::",
"::fe80:1295:8030:49ec:1fc6:57fa",
"fe80:1295:8030:49ec:1fc6:57fa:a::",
"::b:fe80:1295:8030:49ec:1fc6:57fa",
"fe80:1295:8030:49ec:1fc6:57fa:2222::",
"::1295:8030:49ec:1fc6:57fa:2222:0000",
"::8030:49ec:1fc6:57fa:2222:0000",
"fe80:1295:8030:49ec:1fc6:57fa:0000:0000"
};
}
//验证ipv4
String[] examples = {
"192.168.1.1",
"192.0.1.1",
"999.168.1.1",
"192.168.1.1.0",
"192.168.256.1",
"0.0.0.0",
"0000",
"1",
"0.1.1.1"
};
for(String example:examples){
System.out.print("IPAddressUtil:"+IPAddressUtil.isIPv4LiteralAddress(example));
System.out.print(" selfMethod:"+isIPv4Address(example));
if(IPAddressUtil.isIPv4LiteralAddress(example)==isIPv4Address(example)){
System.out.print("自定义正则判断无误!");
}else{
System.out.print("自定义正则判断失准!");
}
System.out.println(" "+example);
}
//验证ipv6
String[] ipAddrs =null;
for(String ipAddr:ipAddrs){
Map result = null;
boolean selfMethod = Boolean.valueOf(result.get("value").toString());
//官方判断IPv6地址方法
boolean sunMethod = IPAddressUtil.isIPv6LiteralAddress(ipAddr);
if(selfMethod==sunMethod){
System.out.print("##############自定义正则匹配结果与官方结果相同----判断一致!");
}else{
System.out.print("##############自定义正则匹配结果与官方结果相同----正则错误!");
}
System.out.print(" "+result.get("method"));
System.out.print(" IPAddressUtil:"+sunMethod);
if(selfMethod){
System.out.println(" IPv6合法");
System.out.println(IPDBUtil.resolveIp(ipAddr));
}else{
System.out.println(" IPv6非法");
System.out.println(IPDBUtil.resolveIp(ipAddr));
}
System.out.println(" "+ipAddr);
}
Java 转成InetAddress判断IP
其实本质上,这个方法底层也是调用了相同的JDK内部方法,源码分析之后再加。
package mynet;
import java.net.*;
public class MyIP
{
public static void main(String[] args) throws Exception
{
if (args.length == 0)
return;
InetAddress address = InetAddress.getByName(args[0]);
System.out.println("IP: " + address.getHostAddress());
switch (address.getAddress().length)
{
case 4:
System.out.println("根据byte数组长度判断这个IP地址是IPv4地址!");
break;
case 16:
System.out.println("根据byte数组长度判断这个IP地址是IPv6地址!");
break;
}
if (address instanceof Inet4Address)
System.out.println("使用instanceof判断这个IP地址是IPv4地址!");
else if (address instanceof Inet6Address)
System.out.println("使用instanceof判断这个IP地址是IPv6地址!");
}
}
Golang 判断IP地址
Golang中有判断IP地址的工具函数,用起来很方便,而且比较准确。
import (
"fmt"
"net"
)
// 0: invalid ip
// 4: IPv4
// 6: IPv6
func ParseIP(s string) (net.IP, int) {
ip := net.ParseIP(s)
if ip == nil {
return nil, 0
}
for i := 0; i < len(s); i++ {
switch s[i] {
case '.':
return ip, 4
case ':':
return ip, 6
}
}
return nil, 0
}
后话
研究了半天用正则判断IP类型然后调用不同的方法,同时校验了合法性,可以减少后方调用方法时抛出异常的次数。结果mentor跟我说正则判断太慢了,直接根据是否有冒号判断一下是否是IPv6,然后丢给后方的调用方法。后面的方法本身就有IP格式判断的逻辑,没必要自己再写一遍。
真是花了时间,做了吃力不讨好的事情,不过并不是浪费时间,暂且记录一下。