判断字符串是否为IP地址通常都是基于正则表达式实现的,无论是引入外部的依赖包亦或是自己写正则实现,基本都是基于正则表达式实现的判断。然而比较例外的是,jdk自身提供了 Inet4Address.getByName 方法也可以帮助我们实现ip地址的判断。本文将详细列举常见的判断字符串是否为IPV4,IPV6地址的方式,并分析其存在的局限性。

一、判断是否为IPV4,IPV6地址的常见方式

1. 使用Apache Commons Validator做判断

需要引入依赖包

    <dependency>
      <groupId>commons-validator</groupId>
      <artifactId>commons-validator</artifactId>
      <version>1.6</version>
    </dependency>

有了依赖包,后续调用InetAddressValidator的核心API就好了。

1.1判断是否为IPV4地址

    private static final InetAddressValidator VALIDATOR = InetAddressValidator.getInstance();
    public static boolean isValidIPV4ByValidator(String inetAddress) {
        return VALIDATOR.isValidInet4Address(inetAddress);

1.2判断是否为IPV6地址

    public static boolean isValidIPV6ByValidator(String inetAddress) {
        return VALIDATOR.isValidInet6Address(inetAddress);

1.3判断是否为IPV6或者IPV4地址

    public static boolean isValidIPV6ByValidator(String inetAddress) {
        return VALIDATOR.isValid(inetAddress);

2. 使用Guava做判断

引入依赖包

    <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>30.0-jre</version>
    </dependency>

调用InetAddresses.isInetAddress即可实现快速的判断,但这个方式能同时判断字符串是否为IPV4或者IPV6地址,如果你只想判断其中一种格式,那就不行了。

    public static boolean isValidByGuava(String ip) {
        return InetAddresses.isInetAddress(ip);

3. 使用OWASP正则表达式做判断

OWASP提供了一系列用于校验常见web应用名词的正则表达式,通过OWASP_Validation_Regex_Repository你可以检索到他们。这个判断方式只能判断是否为IPV4地址。

    private static final String OWASP_IPV4_REGEX =
            "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
                    "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
                    "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
                    "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
    private static final Pattern OWASP_IPv4_PATTERN = Pattern.compile(OWASP_IPV4_REGEX);
    public static boolean isValidIPV4ByOWASP(String ip) {
        if (ip == null || ip.trim().isEmpty()) {
            return false;
        return OWASP_IPv4_PATTERN.matcher(ip).matches();

4. 使用自定义正则表达式做判断

如下通过自定义的正则表达式判断字符串是否为IPV4地址,它的正则表达式以及实现细节,其实和第一种方案中判断IPV4是一致的,如果你只想判断字符串是否为IPV4地址,又懒得引入外部包,那么3,4这两种方式适合你。

    private static final String IPV4_REGEX =
            "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$";
    private static final Pattern IPv4_PATTERN = Pattern.compile(IPV4_REGEX);
    public static boolean isValidIPV4ByCustomRegex(String ip) {
        if (ip == null || ip.trim().isEmpty()) {
            return false;
        if (!IPv4_PATTERN.matcher(ip).matches()) {
            return false;
        String[] parts = ip.split("\\.");
        try {
            for (String segment : parts) {
                if (Integer.parseInt(segment) > 255 ||
                        (segment.length() > 1 && segment.startsWith("0"))) {
                    return false;
        } catch (NumberFormatException e) {
            return false;
        return true;

5. 使用JDK内置的Inet4Address做判断

JDK从1.4版本开始就提供了Inet4Address类实现对IP的各项校验操作,结合该类的getByNamegetHostAddress方法可实现IP地址判断,但是频繁的调用这两个方法会产生一定的性能问题。以下是通过JDK判断字符串是否为IPV4地址的方式:

    public static boolean isValidIPV4ByJDK(String ip) {
        try {
            return Inet4Address.getByName(ip)
                    .getHostAddress().equals(ip);
        } catch (UnknownHostException ex) {
            return false;

二、并不适合ping命令

1. IPV4的标准格式

本文列举的几种判断方式都是针对标准的IP地址而言,标准指的是IP地址由4位通过逗号分割的8bit长度的数字字符串组成,由于每位数字只有8bit长度,所以每个数字的值应该在0~255范围内。相关文档可以参考RFC5321
在这里插入图片描述

2. 有效性验证

我们选举几组字符串,有缺少位数的,有数字以0开头的,也有一组是符合标准格式的。然后通过之前列举的方法判断是否为有效的IP地址。

测试过程就不再赘述,直接将测试用例和测试结果汇总成如下的表格:

用例isValidIPV4ByValidatorisValidIPV6ByValidatorisValidByGuavaisValidIPV4ByOWASPisValidIPV4ByCustomRegexisValidIPV4ByJDK
172.8.9.28truefalsetruetruetruetrue
192.168.0.072falsefalsefalsetruefalsefalse
172.08.9.28falsefalsefalsetruefalsefalse
172.9.28falsefalsefalsefalsefalsefalse
192.168.072falsefalsefalsefalsefalsefalse
192.168.1falsefalsefalsefalsefalsefalse
2001:0db8:85a3:0000:0000:8a2e:0370:7334falsetruetruefalsefalsefalse

通过这7个测试用例中,不难看出:

  • 第1个IP刚好是4位,每位都在0~255之间,且没有任何一位以0开头。所有判断IPV4的方法都返回了true,符合预期。
  • 第2,3个IP也都是4位地址,但是某一位出现以0开始的数字,此时采用OWASP正则表达式的方式返回了true,其他方法都返回了false。
  • 第4,5,6个IP都是3位地址,所有方法返回了false。
  • 最后一个是合法的ipv6地址,我们通过Apache Commons Validator或者Guava包提供的判断方法能够正常返回true。

3. 性能对比

本文在列举的第5个判断方法时特意提到了性能问题,那么使用Inet4Address判断IP地址到底会导致多大的性能损耗呢?实验证明,当判断使用大规模非法IP地址做输入,该方法的性能损耗将不敢想象!

下面将通过一项测试来验证这个结论。

    private static List<String> generateFakeIp(int capacity) {
        List<String> ipList = new ArrayList<String>(capacity);
        for (int i = 0; i < capacity; i++) {
            int parts = boundRandom(1, 3);
            if (chanceOf50()) { //each ip has 50% chance to be 4 parts
                parts = 4;
            StringBuilder sbBuilder = new StringBuilder();
            for (int j = 0; j < parts; j++) {
                if (sbBuilder.length() > 0) {
                    sbBuilder.append(".");
                StringBuilder stringBuilder = new StringBuilder();
                if (chanceOf10()) { //each part has 10% chance to generate a fake number
                    stringBuilder.append('a');
                } else { //each part has 90% chance to generate the correct number
                    stringBuilder.append(boundRandom(0, 255));
                sbBuilder.append(stringBuilder);
            ipList.add(sbBuilder.toString());
        return ipList;
    private static long correctCount(List<String> ipList) {
        return ipList.stream().filter(ip -> isValidIPV4ByCustomRegex(ip)).collect(Collectors.toList()).size();
    // 50% chance
    private static boolean chanceOf50() {
        return boundRandom(0, 9) < 5;
    // 10% chance
    private static boolean chanceOf10() {
        return boundRandom(0, 9) < 1;
    private static Random random = new Random();
    // random int between [start, end], both start and end are included
    private static int boundRandom(int start, int end) {
        return start + random.nextInt(end);

我们通过上面的generateFakeIp方法来产生一批随机的IP地址,这些IP中有正确格式的,也有非法格式的。

主体测试方法如下,该方法将比较isValidIPV4ByCustomRegexisValidIPV4ByJDK这两种判断IP地址的总耗时,以分析性能问题。

    public static void performanceTest() {
        List<String> ipList = generateFakeIp(100);
        double chance = correctCount(ipList);
        System.out.println("start testing, correct ip count is : " + chance);
        long t1 = System.currentTimeMillis();
        ipList.stream().forEach( ip-> isValidIPV4ByCustomRegex(ip));
        long t2 = System.currentTimeMillis();
        ipList.stream().forEach( ip-> isValidIPV4ByJDK(ip));
        long t3 = System.currentTimeMillis();
        System.out.println("isValidIPV4ByCustomRegex cost time : " + (t2-t1));
        System.out.println("isValidIPV4ByJDK cost time : " + (t3-t2));

直接运行后,打印以下结果。

start testing, correct ip count is : 37.0
isValidIPV4ByCustomRegex cost time : 2
isValidIPV4ByJDK cost time : 13745

可以看到,当100个IP中只有37个是合法IP时,基于正则表达式的判断方法只用了2ms,而基于JDK内置的Inet4Address实现的判断方法却用了13s,这已经不在在同一个数量级了。如果我们将测试基数再扩大,那更加不敢想象,所以实际工作中,千万不要使用Inet4Address来做IP合法性判断。

4. 判断IPV4的方法并不适合ping命令

对于标准IPV4格式的地址来说,以上判断方式是没问题的,但是部分非标准IPV4格式的地址,却能够被ping命令正常解析。

对于ping命令来说,我们这里列举的第2~6个IP地址都是合法的,能够被正常解析。

不妨验证一下:
在这里插入图片描述
可以看出,当我们输入的IP地址中,某一位数字位以0开头,那么也能被正常解析,从图片可以看出192.168.0.072被解析成了192.168.0.58172.08.9.28被解析成了172.08.9.28。这是为什么呢?

当ping命令接收的IP地址中,出现以0开头的数字位,那么ping命令将尝试以八进制解析该位,八进制的072,即对应十进制的58,所以192.168.0.072就被解析成了192.168.0.58

如果以0开头的数字位,不符合八进制的格式,则依然以十进制对待该数字位,并忽略最高位的0,由于172.08.9.2808并不是一个合法的八进制数,所以依然按十进制对待并忽略最高位的0,即实际解析成172.8.9.28

此外,当输入的IP地址并不是以逗号分割的四位,ping命令依然能够正常解析。分别ping 196.168.072192.168196时,实际被解析成了 196.168.0.072196.0.0.1680.0.0.192
在这里插入图片描述
可以看出,当IP不足四位时,ping命令会在合适的位置补0,其规律如下所示:

1 part  (ping A)       : 0.0.0.A
2 parts (ping A.B)     : A.0.0.B
3 parts (ping A.B.C)   : A.B.0.C
4 parts (ping A.B.C.D) : A.B.C.D

这几种判断字符串是否为IPV4或者IPV6地址的方式,其内在实现原理都大同小异,除了最后一个方案外都是用正则表达式来实现的。

但是基于正则表达式实现的方法并不能很友好地处理非十进制数字位的情况,而ping命令能够接收的字符串远比这个复杂的多,如果你想通过Java来实现判断ping命令后面的地址是否是合法的IP,那应该是难于上青天,除非你去弄懂ping命令的底层源码。

当然在现实业务场景中,我们判断字符串是否为合法IP地址一般都是基于其标准格式来操作的,也不用担心文中的方法不靠谱,除了第3和最后一个方案外,大胆用吧!

判断字符串是否为IP地址通常都是基于正则表达式实现的,无论是引入外部的依赖包亦或是自己写正则实现,基本都是基于正则表达式实现的判断。然而比较例外的是,jdk自身提供了`Inet4Address.getByName`方法也可以帮助我们实现ip地址的判断。本文将详细列举常见的判断字符串是否为IPV4,IPV6地址的方式,并分析其存在的局限性。
该工具可解析输入域名下对应的所有IP地址(包括IPV4地址IPV6地址),自己写来用的,分享给大家,提供的是jar包,未混淆代码,可以工具查看源码,如果需要可编译的源码可私聊我。 解析域名:www.baidu.com IP地址:112.80.248.75 IP地址:112.80.248.76 解析域名:www.taobao.com IP地址:113.207.33.219 IP地址:113.207.33.220 IP地址:113.207.45.52 IP地址:58.144.252.225 IP地址:2408:8764:0:8:3:0:0:3fc IP地址:2408:8764:0:8:3:0:0:3fb IP地址:2408:8764:0:7:3:0:0:3f9 IP地址:2408:8764:0:7:3:0:0:3fa 编写一个函数来验证输入的字符串是否是有效的 IPv4IPv6 地址 IPv4 地址由十进制数和点来表示,每个地址包含4个十进制数,其范围为 0 - 255, 用(".")分割。比如,172.16.254.1; 同时,IPv4 地址内的数不会以 0 开头。比如,地址 172.16.254.01 是不合法的。 IPv6 地址由8组16进制的数字来表示,每组表示 16 比特。这些组数字通过 (":")分割。比如, 2001:0db8:85a3:0000:0000:8a2e:03
$valid = \Ritaswc\ZxIPAddress\IPv4Tool::isValidAddress('114.114.114.114'); $valid = true; $valid = \Ritaswc\ZxIPAddress\IPv6Tool::isValidAddress('240e:e9:8819:0:3::3f9'); $valid = true; public class IPAddressRegularExpression { public final static String IPV4 = "^((25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)\\.){3}(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)$"; public final static String IPV6 = //8组,无缩减 或 7组:: 当我们通过request获取客户端IP时,自身服务器通常会为了保护信息或者负载均衡的目的,对自身服务器做反向代理。此时如果我们通过request.getRemoteAddr();可能获取到的是自身代理服务器的IP,而无法达到获取用户请求ip的目的。 解决办法: 以下整理了各个代理服务器自己开发的转发服务请求头,这些请求头都不是标准的http请求头,不一定所有的代理都会带上这些请求头,所以通...
java内置的IPAddressUtil可以完成常用的Ipv4ipv6的截取,判断方法,可直接使用。 java判断ipv4ipv6代码 import sun.net.util.IPAddressUtil; public class ipCheck { public static void main(String[] args) { // String ip = "127.0.0.1"; // String ip = "abcd::abcd:abcd:abcd:ab
public static void main(String[] args) { System.out.println("请输入要验证的IP地址"); Scanner sc = new Scanner(System.in); String ipStr = sc.next(); boolean isIpLegal = isIpLe... 编写一个函数来验证输入的字符串是否是有效的 IPv4IPv6 地址IPv4 地址由十进制数和点来表示,每个地址包含4个十进制数,其范围为 0 - 255, 用(“.”)分割。比如,172.16.254.1; 同时,IPv4 地址内的数不会以 0 开头。比如,地址 172.16.254.01 是不合法的。 IPv6 地址由8组16进制的数字来表示,每组表示 16 比特。这...
IPv4 地址由十进制数和点来表示,每个地址包含4个十进制数,其范围为 0 - 255, 用(".")分割。比如,172.16.254.1;同时,IPv4 地址内的数不会以 0 开头。比如,地址 172.16.254.01 是不合法的。 IPv6 地址由8组16进制的数字来表示,每组表示 16 比特。这些组数字通过 (":")分割。比如, 2001:0db8:85a3:0000:0000:8a2e:0370:7334 是一个有效的地址。而且,我们可以加入一些以 0 开头的数字,字母可以使用大写,也可以是小写。所以,2001:db8:85a3:0:0:8A2E:0370:7334 也是一个有效的 IPv6 address地址。 然而,我们不能因为某个组的值为 0,而使用一个空的组,以至于出现 (::) 的情况。 比如, 2001:0db8:85a3::8A2E:0370:7334 是无效的 IPv6 地址。 同时,在 IPv6 地址中,多余的 0 也是不被允许的。比如,02001:0db8:85a3:0000:0000:8a2e:0370::7334 是无效的 IPv6 地址
需要Java 6或更高版本 需要Java 8或更高版本 需要Java 8或更高版本,具有MAC地址支持,与IPv6的EUI-48和EUI-64 MAC集成,新地址框架,新的IP字符串格式已解析和生成以及其他附加功能 org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: org.apache.commons.validator.routines.InetAddressValidator.isValidInet6Address Caused by: java.l.. - `2[0-4]\d` 表示 200-249 之间的数字 - `|` 表示或者 - `[01]?\d\d?` 表示 0-199 之间的数字,可以是 1-3 位数,其中第一位可以是 0 或 1 - `)` 表示一个分组的结束 - `\.` 表示一个点号 - `{3}` 表示前面的分组重复 3 次 - `(25[0-5]|2[0-4]\d|[01]?\d\d?)` 表示最后一组数字,可以是 0-255 之间的数字 - `$` 表示字符串的结尾 这个正则表达式可以匹配如下的 IPv4 地址: - 192.168.0.1 - 10.0.0.1 - 172.16.0.1 - 255.255.255.255 如果字符串不是 IPv4 地址,则不会匹配。