Java:validation并将“host:port”转换为InetSocketAddress的常用方法?
Java中用于validation和转换forms为host:port
的字符串为InetSocketAddress
实例的InetSocketAddress
什么?
如果满足以下条件,那就太好了:
-
没有地址查找;
-
适用于IPv4,IPv6和“字符串”主机名;
(对于IPv4,它是ip:port
,对于IPv6,它是[ip]:port
,对吗?是否有一些RFC定义了所有这些方案?) -
最好不要手工解析字符串。
(我正在考虑所有这些特殊情况,当有人认为他知道套接字地址的所有有效forms时,却会忘记导致意外结果的“特殊情况”。)
我自己提出了一种可行的解决方案。
将字符串转换为URI(这将自动validation它),然后查询URI的主机和端口组件。
遗憾的是,带有主机组件的URI必须有一个方案。 这就是为什么这个解决方案“不完美”的原因。
String string = ... // some string which has to be validated String host; int port; try { // WORKAROUND: add any scheme to make the resulting URI valid. URI uri = new URI("my://" + string); // may throw URISyntaxException host = uri.getHost(); port = uri.getPort(); if (uri.getHost() == null || uri.getPort() == -1) { throw new URISyntaxException(uri.toString(), "URI must have host and port parts"); } // here, additional checks can be performed, such as // presence of path, query, fragment, ... } catch (URISyntaxException ex) { // validation failed } // validation succeeded
此解决方案不需要自定义字符串解析 ,适用于IPv4 ( 1.1.1.1:123
), IPv6 ( [::0]:123
)和主机名 ( my.host.com:123
)。
无意中,此解决方案非常适合我的场景。 无论如何,我打算使用URI方案。
正则表达式会非常巧妙地做到这一点:
Pattern p = Pattern.compile("^\\s*(.*?):(\\d+)\\s*$"); Matcher m = p.matcher("127.0.0.1:8080"); if (m.matches()) { String host = m.group(1); int port = Integer.parseInt(m.group(2)); }
您可以通过多种方式实现此目的,例如使端口可选或在主机上进行一些validation。
它没有完全回答这个问题,但是这个答案对于像我这样只想解析主机和端口的其他人来说仍然很有用,但不一定是完整的InetAddress
。 Guava有一个带有parseString
方法的HostAndPort类。
另一个人给出了一个正则表达式的答案,这是我在最初询问有关主机的问题时正在做的事情。 我仍然会这样做,因为它是一个更高级的正则表达式的例子,可以帮助确定你正在处理什么样的地址。
String ipPattern = "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d+)"; String ipV6Pattern = "\\[([a-zA-Z0-9:]+)\\]:(\\d+)"; String hostPattern = "([\\w\\.\\-]+):(\\d+)"; // note will allow _ in host name Pattern p = Pattern.compile( ipPattern + "|" + ipV6Pattern + "|" + hostPattern ); Matcher m = p.matcher( someString ); if( m.matches() ) { if( m.group(1) != null ) { // group(1) IP address, group(2) is port } else if( m.group(3) != null ) { // group(3) is IPv6 address, group(4) is port } else if( m.group(5) != null ) { // group(5) is hostname, group(6) is port } else { // Not a valid address } }
修改以使端口是可选的非常简单。 将“:( \ d +)”换成“(?::(\ d +))?” 然后检查组(2)等的null。
编辑:我会注意到我没有“共同的方式”方式,但如果必须的话,上面就是我如何做到的。
另请注意:如果主机和IPv4实例的处理方式相同,则可以删除IPv4案例。 我把它们分开了,因为如果你知道你有IP地址,有时你可以避免最终的主机查找。
new InetSocketAddress( addressString.substring(0, addressString.lastIndexOf(":")), Integer.parseInt(addressString.substring(addressString.lastIndexOf(":")+1, addressString.length));
? 我可能犯了一些小傻话。 而且我假设你只想要一个新的InetSocketAddress对象,只有那种格式的String。 主持人:端口
各种奇特的hackery,以及其他地方提供的优雅但不安全的解决方案。 有时,不优雅的蛮力解决方案就是这样。
public static InetSocketAddress parseInetSocketAddress(String addressAndPort) throws IllegalArgumentException { int portPosition = addressAndPort.length(); int portNumber = 0; while (portPosition > 1 && Character.isDigit(addressAndPort.charAt(portPosition-1))) { --portPosition; } String address; if (portPosition > 1 && addressAndPort.charAt(portPosition-1) == ':') { try { portNumber = Integer.parseInt(addressAndPort.substring(portPosition)); } catch (NumberFormatException ignored) { throw new IllegalArgumentException("Invalid port number."); } address = addressAndPort.substring(0,portPosition-1); } else { portNumber = 0; address = addressAndPort; } return new InetSocketAddress(address,portNumber); }