如何判断一个字符串是IP地址还是主机名

4

您有一个从管理Web UI检索的字符串(因此它肯定是字符串)。如何在Java中找出这个字符串是IP地址还是主机名?

更新:我想我没有表达清楚,我更想知道Java SDK中是否有任何可以用来区分IP和主机名的东西?对于造成的困惑,我很抱歉,感谢所有花费/将要花费时间回答此问题的人。

9个回答

15

您可以使用此模式的正则表达式:

\b(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]?)\b

那将告诉您它是否为IPv4地址。


我认为仅测试数字模式就足够了,如果需要真正的范围验证,使用正则表达式并不是最好的方法。 - DevelopingChris
不起作用。当我将您的正则表达式复制并粘贴到 https://regex101.com 中,并搜索字符串 192.168.1.1 时,它显示没有匹配项。 - Jesse Barnum

2
你可以检查字符串是否符合数字.数字.数字.数字的格式,例如:
\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b

这个表达式可以匹配从0-999的任何数字。

其他情况下,可以默认使用主机名。


999.999.999.999 不是有效的 IP 格式。您的正则表达式需要更加具体。 - Vagnerr
5
不认为这是一个有效的主机名。 - DevelopingChris

2

我们可以假设它是其中之一,而不是完全不同的东西吗?如果是这样,我可能会使用正则表达式来查看它是否匹配“点分四段”格式。


1

这并不像看起来那么简单,字符如连字符、下划线和方括号 '-'、'_'、'[]' 存在一些歧义。

Java SDK 在这个领域存在一些限制。当使用 InetAddress.getByName 时,它将走出网络进行 DNS 名称解析并解析地址,这是昂贵且不必要的,如果你只想检测主机与地址的区别。此外,如果一个地址以略微不同但有效的格式编写(IPv6 中常见),对 InetAddress.getByName 的结果进行字符串比较将无法工作。

IPAddress Java 库 可以解决这个问题。链接提供了 javadoc。免责声明:我是项目经理。

static void check(HostName host) {
    try {
        host.validate();
        if(host.isAddress()) {
            System.out.println("address: " + host.asAddress());
        } else {
            System.out.println("host name: " + host);
        }
    } catch(HostNameException e) {
        System.out.println(e.getMessage());
    }
}

public static void main(String[] args) {
    HostName host = new HostName("1.2.3.4");
    check(host);
    host = new HostName("1.2.a.4");
    check(host);
    host = new HostName("::1");
    check(host);
    host = new HostName("[::1]");
    check(host);
    host = new HostName("1.2.?.4");
    check(host);  
}

输出:

address: 1.2.3.4
host name: 1.2.a.4
address: ::1
address: ::1
1.2.?.4 Host error: invalid character at index 4

1
URI validator = new URI(yourString);

这段代码将验证IP地址或主机名。(如果字符串无效,它会抛出一个格式错误的URI异常)

如果您试图区分这两者...那么我误读了您的问题。


1

你可以在 InetAddress.getByName(addr) 调用中使用安全管理器。

如果 addr 不是点分十进制表示的 IP 地址,则 getByName 将尝试执行一个连接以进行名称查找,此时安全管理器可以捕获作为 checkConnect(addr, -1) 调用的结果,从而抛出 SecurityException 异常,你可以捕获该异常。

如果你具有完全特权,可以使用 System.setSecurityManager() 在执行 getByName 调用之前插入自定义的安全管理器。


0
使用 InetAddress#getAllByName(String hostOrIp) - 如果 hostOrIp 是一个 IP 地址,结果将是一个包含单个 InetAddress 的数组,它的 .getHostAddress() 返回与 hostOrIp 相同的字符串。
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

public class IPvsHostTest {
    private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger(IPvsHostTest.class);

    @org.junit.Test
    public void checkHostValidity() {
        Arrays.asList("10.10.10.10", "google.com").forEach( hostname -> isHost(hostname));
    }
    private void isHost(String ip){
        try {
            InetAddress[] ips = InetAddress.getAllByName(ip);
            LOG.info("IP-addresses for {}", ip);
            Arrays.asList(ips).forEach( ia -> {
                LOG.info(ia.getHostAddress());
            });
        } catch (UnknownHostException e) {
            LOG.error("Invalid hostname", e);
        }
    }
}

输出:

IP-addresses for 10.10.10.10
10.10.10.10
IP-addresses for google.com
64.233.164.100
64.233.164.138
64.233.164.139
64.233.164.113
64.233.164.102
64.233.164.101

0

你不能只对其进行正则表达式匹配吗?


0

这段代码仍然会执行DNS查找,如果指定了主机名,但至少它跳过了可能在其他方法中执行的反向查找:

   ...
   isDottedQuad("1.2.3.4");
   isDottedQuad("google.com");
   ...

boolean isDottedQuad(String hostOrIP) throws UnknownHostException {
   InetAddress inet = InetAddress.getByName(hostOrIP);
   boolean b = inet.toString().startsWith("/");
   System.out.println("Is " + hostOrIP + " dotted quad? " + b + " (" + inet.toString() + ")");
   return b;
}

它生成以下输出:

Is 1.2.3.4 dotted quad? true (/1.2.3.4)
Is google.com dotted quad? false (google.com/172.217.12.238)

你觉得我们能否期待 toString() 行为很快会发生改变吗?

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接