【问题标题】:Validating IPv4 string in Java在 Java 中验证 IPv4 字符串
【发布时间】:2011-06-02 16:26:21
【问题描述】:

Bellow 方法正在验证字符串是否正确 IPv4 地址,如果有效则返回 true。非常感谢正则表达式和优雅的任何改进:

public static boolean validIP(String ip) {
    if (ip == null || ip.isEmpty()) return false;
    ip = ip.trim();
    if ((ip.length() < 6) & (ip.length() > 15)) return false;

    try {
        Pattern pattern = Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
        Matcher matcher = pattern.matcher(ip);
        return matcher.matches();
    } catch (PatternSyntaxException ex) {
        return false;
    }
}

【问题讨论】:

  • 可能有不同的表示形式。您正在寻找点十进制表示法:请参阅 en.wikipedia.org/wiki/IPv4#Address_representations
  • 7 的 ip.length() 有效,例如 0.0.0.0 是 7 个字符,您的方法将返回 false。
  • 我想知道为什么这仍然不是某些第三方库的一部分。我的意思是避免使用第三方库是好的,但为此计算正则表达式非常容易出错,我认为这比使用第三方 API 更糟糕。
  • 正则表达式很好,您只需将[0-9] 替换为\d 并在方法之外进行Pattern 编译,这样您就不必每次都编译它。

标签: java string validation ip


【解决方案1】:

更新: Commons-HttpClient 及其后继 HttpComponents-HttpClient 已采用此功能。你可以像这样使用它的这个版本:InetAddressUtils.isIPv4Address(Str)


Apache Commons Validator 的开发版本有一个 InetAddressValidator 类,它有一个 isValidInet4Address(String) 方法来执行检查以查看给定的 String 是一个有效的 IPv4 地址。

source code 可以从存储库中查看,因此如果您觉得有任何改进意见,可能会提供一些改进意见。

快速浏览所提供的代码表明您的方法在每次调用该方法时都会编译一个Pattern。我会将 Pattern 类移到 static 字段中,以避免每次调用时进行昂贵的模式编译过程。

【讨论】:

  • InetAddressUtils.isIPv4Address(Str) 是 java 内部 API。你不应该使用它。
  • @user918888 这是什么?它所做的只是将它与正则表达式匹配并返回结果。
  • 它是 apache 公共库的一部分,很好。也许他认为这是一些“com.sun”类型的内部东西?
【解决方案2】:

如果您不介意对www.example.com 等无效IP 地址使用dns 解析,可以使用InetAddress 方法进行检查:

public static final boolean checkIPv4(final String ip) {
    boolean isIPv4;
    try {
    final InetAddress inet = InetAddress.getByName(ip);
    isIPv4 = inet.getHostAddress().equals(ip)
            && inet instanceof Inet4Address;
    } catch (final UnknownHostException e) {
    isIPv4 = false;
    }
    return isIPv4;
}

该方法检查它是否是 Inet4Address 的实例,以及参数是否是 IP 地址而不是主机名。 如果您希望有很多主机名作为参数,请注意此实现使用 DNS 来尝试解析它。这可能是性能问题。

否则你可以有一个高峰 boolean sun.net.util.IPAddressUtil.isIPv4LiteralAddress(String src) 如何解析字符串以进行 IPv4 检查。

【讨论】:

  • FWIW,这就是我通常做的:首先使用一个简单的正则表达式来检查参数是否由 4 个点分隔的数字组成(我不尝试检查数字是否在范围内等,只是一般格式)。如果第一次检查通过,那么我使用 InetAddress 作为这个答案来完成验证。
【解决方案3】:

这是一种更易于阅读、效率稍低的方法。

public static boolean validIP (String ip) {
    try {
        if ( ip == null || ip.isEmpty() ) {
            return false;
        }

        String[] parts = ip.split( "\\." );
        if ( parts.length != 4 ) {
            return false;
        }

        for ( String s : parts ) {
            int i = Integer.parseInt( s );
            if ( (i < 0) || (i > 255) ) {
                return false;
            }
        }
        if ( ip.endsWith(".") ) {
            return false;
        }

        return true;
    } catch (NumberFormatException nfe) {
        return false;
    }
}

【讨论】:

  • 我更喜欢这种方法。
  • 如果你做ip.split("\\.", -1)你可以删除if (ip.endsWith(".")) ...
  • "192.168.1" 是一个有效的 IP 地址。我认为你的方法有缺陷。
  • @PEdroArthur 四点数字是 IPV4 地址的标准表示。点分八位字节格式从未在 RFC 中真正指定过。第一次提到是在 MTP RFC(大约 1981 年)中说,“四个用点分隔并用括号括起来的小十进制整数,例如,“[123.255.37.321]”,它表示四个八位字段中的 32 位 ARPA Internet 地址。”您是否有任何来源表明“192.168.1”有效?
  • @rouble 我不知道。但是,我使用的所有网络堆栈都接受像 192.168.1 (192.168.0.1) 和 10.1 (10.0.0.1) 这样的地址。
【解决方案4】:

如果你想使用一个支持 IPv4 和 IPv6 的已发布版本的库,你可以使用Guava

boolean isValid = InetAddresses.isInetAddress("1.2.3.4");

【讨论】:

    【解决方案5】:

    如果您使用问题中的代码,则需要更改行:

    if ((ip.length() < 6) & (ip.length() > 15)) return false;
    

    if ((ip.length() <= 6) || (ip.length() > 15)) return false;
    

    【讨论】:

      【解决方案6】:

      this查找正则表达式示例

      package com.mkyong.regex;
      
      import java.util.regex.Matcher;
      import java.util.regex.Pattern;
      
      public class IPAddressValidator{
      
          private Pattern pattern;
          private Matcher matcher;
      
          private static final String IPADDRESS_PATTERN = 
              "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
              "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
              "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
              "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";
      
          public IPAddressValidator(){
            pattern = Pattern.compile(IPADDRESS_PATTERN);
          }
      
         /**
          * Validate ip address with regular expression
          * @param ip ip address for validation
          * @return true valid ip address, false invalid ip address
          */
          public boolean validate(final String ip){         
            matcher = pattern.matcher(ip);
            return matcher.matches();             
          }
      }
      

      【讨论】:

      • 我不认为这在所有情况下都是正确的。此方法为 172.013.1.2 返回 true,这是一个无效的 IP 地址字符串。它应该是 172.13.1.2。
      • @radiantRazor 172.013.1.2 是一个有效地址,尽管可能会产生误导,因为 013 通常被解释为八进制,即 11。请参阅 [点十进制表示法] (en.wikipedia.org/wiki/Dot-decimal_notation)。
      【解决方案7】:

      这里有一个解决方案,它使用 Java 8 流来检查地址是否由 0 到 255 之间的 4 个数字组成,用点分隔:

      public class IpValidation {
      
          /**
           * Check that a string represents a decimal number
           * @param string The string to check
           * @return true if string consists of only numbers without leading zeroes, false otherwise
           */
          public static boolean isDecimal(String string) {
              // Check whether string has a leading zero but is not "0"
              if (string.startsWith("0")) {
                  return string.length() == 1;
              }
              for(char c : string.toCharArray()) {
                  if(c < '0' || c > '9') {
                      return false;
                  }
              }
              return true;
          }
      
          public static boolean isIp(String string) {
              String[] parts = string.split("\\.", -1);
              return parts.length == 4 // 4 parts
                      && Arrays.stream(parts)
                      .filter(IpValidation::isDecimal) // Only decimal numbers
                      .map(Integer::parseInt)
                      .filter(i -> i <= 255 && i >= 0) // Must be inside [0, 255]
                      .count() == 4; // 4 numerical parts inside [0, 255]
          }
      }
      

      【讨论】:

      • 我不认为这在所有情况下都是正确的。此方法为 172.013.1.2 返回 true,这是一个无效的 IP 地址字符串。它应该是 172.13.1.2。可以修改“isNumeric”方法来纠正这个问题。
      • 172.013.1.2 一个有效的 IP 地址。和 172.013.001.002 一样。但通常避免使用前导零,因为它们可能暗示数字是八进制而不是十进制。
      • ping 192.168.1.09 ping: cannot resolve 192.168.1.09: Unknown host 摘要:IP段以0开头表示该段是八进制的,所以我们还需要验证其余数字不包含7 ,8(我承认我以前不知道这一点:那是 yi 给出的示例为 172.013.1.2 正确的测试用例是 172.13.1.008 - 它失败了)长话短说:上述验证不正确,可能导致误报
      • "在一种情况下有效,这对我来说已经足够了。"这是反对投票的充分理由。很遗憾,这个答案得到了赏金。一种情况起作用的原因是,以0(在这种情况下为001)开头的数字在八进制和十进制中的含义相同。仅当只有一个非零数字且其后没有零且该数字不是 8 或 9 时才是正确的。哎呀。
      【解决方案8】:

      认为这个解决方案相当优雅,虽然需要一分钟才能理解。

      基本思想是获取一个子字符串,然后对其进行验证。
      请注意,我切换了 x 和 y,因为子字符串的开头总是最后一个加 1 的结尾。

      此解决方案比正则表达式变体快约 20 倍,比拆分快 2 倍。

      public static boolean validIP(String ip) {
          if(ip == null || ip.length() < 7 || ip.length() > 15) return false;
      
          try {
              int x = 0;
              int y = ip.indexOf('.');
      
              if (y == -1 || ip.charAt(x) == '-' || Integer.parseInt(ip.substring(x, y)) > 255) return false;
      
              x = ip.indexOf('.', ++y);
              if (x == -1 || ip.charAt(y) == '-' || Integer.parseInt(ip.substring(y, x)) > 255) return false;
      
              y = ip.indexOf('.', ++x);
              return  !(y == -1 ||
                      ip.charAt(x) == '-' ||
                      Integer.parseInt(ip.substring(x, y)) > 255 ||
                      ip.charAt(++y) == '-' ||
                      Integer.parseInt(ip.substring(y, ip.length())) > 255 ||
                      ip.charAt(ip.length()-1) == '.');
      
          } catch (NumberFormatException e) {
              return false;
          }
      }
      

      如果您知道自己会有很多错误的 IP,请考虑在第一个 if 下方添加以下代码。这将使代码慢 1.5 倍,但如果出现错误,将其提高 700 倍

          for (int i = 0; i < ip.length(); i++) {
              if (!Character.isDigit(ip.charAt(i)) && ip.charAt(i) != '.') return false;
          }
      

      【讨论】:

      • 上述解决方案是否也适用于 IPv4Address 和 IPv6Address,如果可能,您能否使用 main() 方法添加测试用例
      • 这是我为 IPv4 测试过的最快的功能,但我们也可以为 IPv6 扩展它吗?
      【解决方案9】:

      尝试对 IPv4 使用以下正则表达式

      String ip4Regex="^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})){3}$";
      

      希望对你有帮助:)

      【讨论】:

        【解决方案10】:

        我认为正确的方法是构建一个IPAddress 类并通过给它一个IP地址的字符串表示来实例化它。

        通过这种方式,您可以将不同的验证步骤拆分为实例方法,并获得一些separation of concern

        例如,这里通常是它自己的方法,简单地调用它isEmpty

        return (ip == null || ip.isEmpty());
        

        这应该是一个单独的方法,你可以称之为hasProbableLength

        ip = ip.trim();
        return ((ip.length() < 6) & (ip.length() > 15));
        

        这里发生了很多事情。我会尝试打破它,也许尝试完全跳过正则表达式。

        try {
            Pattern pattern = Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
            Matcher matcher = pattern.matcher(ip);
            return matcher.matches();
        } catch (PatternSyntaxException ex) {
            return false;
        }
        

        我首先将字符串拆分为点,然后看到我得到了四个组。调用这个方法divideIntoGroups

        然后,我将验证每个组的值是否介于 0 和 255 之间。调用此方法 validateGroups

        现在你有了这个,如果你想扩展这个类来查找 IP 是本地主机还是广播地址,这很容易做到。这就是separation of concerns 给你的。

        您还可以准确判断在验证 IP 地址字符串时违反了哪条验证规则。正则表达式不会。

        【讨论】:

        • 能否使用 main() 方法添加 java 代码和测试用例?
        【解决方案11】:

        我发现在 ipv4 中检查给定 ip 是否正确的最简单方法是使用 commons-validator-1.4.0.jar 有类:--

        org.apache.commons.validator.routines.InetAddressValidator
        

        InetAddressValidator

        例如

        InetAddressValidator inetValidator = InetAddressValidator.getInstance();
        inetValidator.isValidInet4Address(yourIPAddress);
        

        使用这个简单的正则表达式:--

          "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$";
        

        【讨论】:

        • 那个正则表达式接受 999.999.999.999 虽然这样是非常没用的
        【解决方案12】:

        我建议使用您正在开发的平台/框架上可用的东西。

        如果您认为没有什么足够好,那么您可能只想将已编译的正则表达式添加到帮助程序类中。例如,here 的 Android 框架是如何做到的:

        public static final Pattern IP_ADDRESS
            = Pattern.compile(
                "((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]"
                + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
                + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
                + "|[1-9][0-9]|[0-9]))");
        

        【讨论】:

          【解决方案13】:

          大多数答案(除了 Michael Konietzka 的答案)都是错误的:例如,10.8 是一个有效的 IP4 地址(10.0.0.8 的简写)。

          参见 Java 规范中的Textual representation of IP addresses

          从参考资料中可以看出,要检查数字表示,可能有 1 到 4 个部分,并且在每种情况下,对这些部分都有不同的限制。

          【讨论】:

            【解决方案14】:
            boolean isValid = inetAddressValidator.isValid(hostName) || hostName.equalsIgnoreCase("localhost");
            

            与 coobird 相同的答案。只需将 localhost 添加到语句中即可。大多数环境都将“localhost”作为有效的主机名。它不是 ipv4 格式,但应包含在内以考虑有效性。

            【讨论】:

              【解决方案15】:

              The IPAddress Java library 会做到的。链接中提供了 javadoc。免责声明:我是项目经理。

              该库透明地支持 IPv4 和 IPv6,因此验证两者的工作方式相同,并且它还支持 CIDR 前缀长度的 IPC4 地址。它支持更不常见的格式,例如 inet_aton(例如,这里的另一个答案中提到了 10.8)

              验证地址是否有效:

                  String str = "1.2.3.4";
                  IPAddressString addrString = new IPAddressString(str);
                  try {
                       IPAddress addr = addrString.toAddress();
                       ...
                  } catch(AddressStringException e) {
                      //e.getMessage provides validation issue
                  }
              

              【讨论】:

                【解决方案16】:
                public static boolean ipv4Check(String ipAddress){
                
                    try{
                        if(ipAddress!=null && !ipAddress.isEmpty()){
                            String [] ip = ipAddress.split("\\.");
                            if(ip.length!=4)
                                return false;
                
                            for(int i=0;i<=ip.length-1;i++){
                                int j=Integer.parseInt(ip[i]);
                                if(j<0 || j>255){
                                    return false;
                                }
                            }
                            if ( ipAddress.endsWith(".") ) {
                                return false;
                            }
                            if ( ipAddress.startsWith(".") ) {
                                return false;
                            }
                
                        }
                        return true;
                    } catch (NumberFormatException ex) {
                        return false;
                    }       
                }
                

                【讨论】:

                • 上述解决方案是否也适用于 IPv4Address 和 IPv6Address,如果可能,您能否添加测试用例?
                • 这是为了检查 ipv4 而不是 ipv6,因为它们的结构不同
                • 你能添加 IPv6Address 的 java 解决方案吗?你能从 java @Amit Kumar 中的 public static void main 方法添加 IPv4Address 和 IPv6Address 的测试用例吗
                【解决方案17】:

                对你有用,

                
                boolean isIPv4Address(String inputString) {
                    String[] splitString = inputString.split("[.]");
                    if (splitString.length > 4) {
                        return false;
                    }
                    for (String string : splitString) {
                        if (string.isEmpty()) {
                            return false;
                        }
                        if (!string.matches("[0-9]{1,3}")) {
                            return false;
                        }
                        int number = Integer.parseInt(string);
                        if (!(number >= 0 && number <= 255)) {
                            return false;
                        }
                    }
                    return true;
                }
                
                

                【讨论】:

                • 上述解决方案是否也适用于 IPv4Address 和 IPv6Address,如果可能,您能否添加测试用例?
                猜你喜欢
                • 2011-08-05
                • 2015-11-06
                • 1970-01-01
                • 2011-04-13
                • 2012-10-02
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多