如何获取给定IP范围外的所有IP及IP段?求算法与类Java示例
如何计算给定IP范围的补集(即不在范围内的IP地址范围)
核心思路
要搞定这个问题,核心是把IP地址转成可直接比较的整数,通过排序合并给定范围,再找出这些范围之间的"间隙",以及整个IP地址空间首尾未被覆盖的部分。具体步骤拆解下:
- 把每个IP范围的起始、结束IP转成32位整数(针对IPv4),这样就能用数值大小判断范围的覆盖关系。
- 给所有给定的IP范围按起始整数从小到大排序。
- 合并重叠或相邻的范围(比如
192.168.0.0-192.168.0.100和192.168.0.90-192.168.0.200要合并成192.168.0.0-192.168.0.200)。 - 遍历合并后的范围,计算相邻范围之间的间隙,同时检查IP空间起点(
0.0.0.0)到第一个范围的起点、最后一个范围的终点到IP空间终点(255.255.255.255)的空隙,这些就是我们要的"不在给定范围内的IP范围"。
Apex(类Java)实现示例
下面是一个完整的Apex工具类,包含IP与整数互转、范围合并、补集计算的方法:
public class IpRangeUtils { // 将IPv4地址转换为32位整数 public static Long ipToLong(String ip) { String[] octets = ip.split('\\.'); if (octets.size() != 4) { throw new IllegalArgumentException('Invalid IPv4 address: ' + ip); } Long result = 0; for (Integer i = 0; i < 4; i++) { Integer octet = Integer.valueOf(octets[i]); if (octet < 0 || octet > 255) { throw new IllegalArgumentException('Invalid octet value: ' + octet); } result |= octet << (24 - (i * 8)); } return result; } // 将32位整数转换回IPv4地址 public static String longToIp(Long ipLong) { if (ipLong < 0 || ipLong > 4294967295L) { throw new IllegalArgumentException('Invalid IP integer value: ' + ipLong); } String[] octets = new String[4]; for (Integer i = 0; i < 4; i++) { octets[i] = String.valueOf((ipLong >> (24 - (i * 8))) & 0xFF); } return String.join(octets, '.'); } // 合并重叠或相邻的IP范围 public static List<Range> mergeRanges(List<Range> ranges) { if (ranges == null || ranges.isEmpty()) { return new List<Range>(); } // 按起始IP的整数排序 ranges.sort((a, b) -> a.startIpLong.compareTo(b.startIpLong)); List<Range> merged = new List<Range>(); Range current = ranges[0]; for (Integer i = 1; i < ranges.size(); i++) { Range next = ranges[i]; // 如果当前范围的结束 >= 下一个范围的起始-1(相邻也算),则合并 if (current.endIpLong >= next.startIpLong - 1) { current.endIpLong = Math.max(current.endIpLong, next.endIpLong); } else { merged.add(current); current = next; } } merged.add(current); return merged; } // 计算给定IP范围的补集(即不在范围内的IP范围) public static List<Range> calculateComplement(List<Range> inputRanges) { List<Range> mergedRanges = mergeRanges(inputRanges); List<Range> complement = new List<Range>(); Long minIp = 0L; // 0.0.0.0 Long maxIp = 4294967295L; // 255.255.255.255 // 处理第一个范围之前的间隙 if (mergedRanges.isEmpty()) { complement.add(new Range(minIp, maxIp)); return complement; } Range first = mergedRanges[0]; if (first.startIpLong > minIp) { complement.add(new Range(minIp, first.startIpLong - 1)); } // 处理中间的间隙 for (Integer i = 1; i < mergedRanges.size(); i++) { Range prev = mergedRanges[i-1]; Range curr = mergedRanges[i]; if (curr.startIpLong > prev.endIpLong + 1) { complement.add(new Range(prev.endIpLong + 1, curr.startIpLong - 1)); } } // 处理最后一个范围之后的间隙 Range last = mergedRanges[mergedRanges.size()-1]; if (last.endIpLong < maxIp) { complement.add(new Range(last.endIpLong + 1, maxIp)); } return complement; } // 辅助类:表示一个IP范围(存储起始和结束的整数形式) public class Range { public Long startIpLong; public Long endIpLong; public Range(Long start, Long end) { this.startIpLong = start; this.endIpLong = end; } public Range(String startIp, String endIp) { this.startIpLong = ipToLong(startIp); this.endIpLong = ipToLong(endIp); } // 输出友好的范围字符串 public String getRangeString() { return longToIp(startIpLong) + ' - ' + longToIp(endIpLong); } } }
使用示例
你可以这样调用这个工具类:
// 定义给定的IP范围 List<IpRangeUtils.Range> inputRanges = new List<IpRangeUtils.Range>{ new IpRangeUtils.Range('192.168.0.0', '192.168.0.100'), new IpRangeUtils.Range('10.0.0.0', '10.0.0.255'), new IpRangeUtils.Range('192.168.0.90', '192.168.0.200') // 这个会和第一个范围合并 }; // 计算补集 List<IpRangeUtils.Range> complementRanges = IpRangeUtils.calculateComplement(inputRanges); // 输出结果 for (IpRangeUtils.Range range : complementRanges) { System.debug('不在给定范围内的IP范围:' + range.getRangeString()); }
注意事项
- 这个实现仅针对IPv4地址,如果需要支持IPv6,得修改IP与整数的转换逻辑(IPv6是128位,需要用更大的数值类型或者字符串处理)。
- 输入的IP范围需要是合法的IPv4地址,否则
ipToLong方法会抛出异常,你可以根据需求添加更完善的校验逻辑。
内容的提问来源于stack exchange,提问作者GoldenAxe
相关产品推荐
相关产品推荐

