public class IpUtils {
private static final String XFF = "x-forwarded-for";
private static final String RANGE = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
private static final Pattern PATTERN = Pattern.compile("^(?:" + RANGE + "\\.){3}" + RANGE + "$");
public static String longToIpv4(long longIp) {
int octet3 = (int) ((longIp >> 24) % 256);
int octet2 = (int) ((longIp >> 16) % 256);
int octet1 = (int) ((longIp >> 8) % 256);
int octet0 = (int) ((longIp) % 256);
return octet3 + "." + octet2 + "." + octet1 + "." + octet0;
}
public static long ipv4ToLong(String ip) {
String[] octets = ip.split("\\.");
return (Long.parseLong(octets[0]) << 24) + (Integer.parseInt(octets[1]) << 16)
+ (Integer.parseInt(octets[2]) << 8) + Integer.parseInt(octets[3]);
}
public static boolean isIpv4Private(String ip) {
long longIp = ipv4ToLong(ip);
return (longIp >= ipv4ToLong("10.0.0.0") && longIp <= ipv4ToLong("10.255.255.255"))
|| (longIp >= ipv4ToLong("172.16.0.0") && longIp <= ipv4ToLong("172.31.255.255"))
|| longIp >= ipv4ToLong("192.168.0.0") && longIp <= ipv4ToLong("192.168.255.255");
}
public static boolean isIpv4(String ip) {
return PATTERN.matcher(ip).matches();
}
}
正则表达式预编译
private static final String RANGE = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
private static final Pattern PATTERN = Pattern.compile("^(?:" + RANGE + "\\.){3}" + RANGE + "$");
把 Pattern 作为一个常量, 类加载阶段初始化它
错误用例:
public boolean isIpv4(String str) {
String range = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
Pattern pattern = Pattern.compile("^(?:" + range + "\\.){3}" + range + "$")
return pattern.matcher(str).matches();
}
原因: 每次进入方法都要编译一次正则表达式, 造成不必要的消耗
算法
public static String longToIpv4(long longIp) {
int octet3 = (int) ((longIp >> 24) % 256);
int octet2 = (int) ((longIp >> 16) % 256);
int octet1 = (int) ((longIp >> 8) % 256);
int octet0 = (int) ((longIp) % 256);
return octet3 + "." + octet2 + "." + octet1 + "." + octet0;
}
public static long ipv4ToLong(String ip) {
String[] octets = ip.split("\\.");
return (Long.parseLong(octets[0]) << 24) + (Integer.parseInt(octets[1]) << 16)
+ (Integer.parseInt(octets[2]) << 8) + Integer.parseInt(octets[3]);
}
刚一入眼, 我以为是 LeetCode 的 restore-ip-addresses, 我还在感叹这么几行代码就能实现, 仔细一看才发现 ipv4ToLong
与 longToIpv4
这两个方法其实是按照一个自定义的标准去实现的.
—
开始分析:
long
类型在 Java 中占 64 位, 方法中只用了低 32 位, 将低 32 位分成 4 个 8 位组
1 个 8 位组刚好能够存储 0-255
4 个 8 位组就能够存储一个 ip 地址
long 转 IPV4 时通过位运算符取出每个 8 位组, 最后拼装成一个 IP 地址.
其中(longIp >> 24) % 256
还可以写作 (longIp >> 24) & 255
IPV4 转 long 时把 ip 分成了 4 份, 每份左移后累加, 最后得到 IP.