IP段转CIDR:原理Java实现

news2025/3/1 19:20:52

🧑 博主简介:CSDN博客专家历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
技术合作请加本人wx(注明来自csdn):foreast_sea

在这里插入图片描述


在这里插入图片描述

IP段转CIDR:Java实现

首先,需要确认CIDR是什么?CIDR表示的是无类别域间路由,通常形式是IP地址后跟一个斜杠和数字,比如192.168.1.0/24。这个数字表示网络前缀的位数,剩下的位数用于主机地址。CIDR的作用是更高效地分配IP地址,减少浪费。

我们现在要实现,将给定的起始IP结束IP转换为CIDR块。那么,如何从两个IP地址中找到覆盖它们的CIDR呢?可能需要分解成多个CIDR块,因为单个CIDR可能无法覆盖整个范围,尤其是当范围不是连续的2的幂次方时。

一、需要考虑以下几点

  1. 将IP地址转换为整数:IPv4地址是32位的,可以将每个部分转换为二进制,然后组合成一个长整数,这样便于计算。例如,192.168.1.0转换为整数是3232235776。

  2. 确定起始和结束IP的整数形式:需要将输入的字符串拆分四个部分,每个部分转换为整数,并进行位移组合成一个长整型。

  3. 计算CIDR块:从起始地址开始,找到最大的前缀长度(即最小的子网),使得该子网包含在起始和结束范围内。然后,将起始地址递增到下一个子网的起始地址,重复这个过程直到覆盖整个范围。

这里的关键步骤是如何找到每个可能的CIDR块。例如,假设起始IP是192.168.0.0,结束IP是192.168.3.255。这个范围可以被分解为192.168.0.0/23或者192.168.0.0/24192.168.1.0/24以及192.168.2.0/23等,但需要找到最合适的分解方式。

二、可能的算法步骤

  • 将起始IP和结束IP转换为长整数。
  • 如果起始IP大于结束IP,则交换它们。
  • 初始化一个列表来保存CIDR块。
  • 循环处理,直到起始IP超过结束IP:
  • 计算起始IP的最低位有效位(即最大的后缀0的位数),这决定了当前可能的最大CIDR块。
  • 同时,确定当前块的大小,不超过结束IP。
  • 确定实际可以使用的掩码位数,使得该子网不超过结束IP。
  • 将CIDR块添加到列表。
  • 将起始IP加上当前块的大小,继续处理。

这里需要注意的是,每次找到的CIDR块应该是最大的可能块,以减少块的数量。例如,如果起始IP是192.168.0.0,结束IP是192.168.0.255,那么CIDR/24。但如果结束IP是192.168.1.127,可能需要分解为多个块,如/24和/25等。

在Java中实现时,需要处理大整数,因为IPv4是32位,可以使用long类型来避免符号问题。同时,掩码的计算可能需要位运算,比如找出最长的公共前缀。

另外,还需要处理一些特殊情况,比如起始和结束IP相同,这时候CIDR就是/32。或者起始IP到结束IP是一个连续的块,但无法被一个CIDR覆盖,需要拆分成多个。

例如,起始IP是192.168.0.5,结束IP是192.168.0.7。这时候CIDR列表应该是192.168.0.5/32, 192.168.0.6/31,或者类似的组合。但需要确保每个CIDR块覆盖的IP范围在起始和结束之间。

三、Java程序实现步骤

现在,需要将这些逻辑转化为Java代码,需要以下步骤:

  1. 编写一个方法将IP字符串转换为长整数

  2. 编写一个方法将长整数转换回IP字符串

  3. 编写方法计算两个IP之间的CIDR块列表

在转换IP到长整数时,需要将每个部分左移相应的位数,例如第一个部分左移24位,第二个左移16位,依此类推。同时,确保每个部分的数值在0-255之间。

在计算CIDR块时,需要找到最大的掩码位数,使得该子网的起始地址加上块大小不超过结束IP。块大小是2^(32 - mask),所以需要找到最大的mask,使得start + size <= end + 1。或者,另一种方式是找到起始IP的最低位有效位,然后逐步调整掩码位数。

3.1 如何计算mask?

可以计算从start到end的差值为diff = end - start +1。然后,最大的块是最大的2的幂次方,不大于diff,并且块的大小还要考虑start的低位是否为0。

或者,找到从start开始的最低位有效位,例如,计算start的二进制中最低位的1的位置,然后块的大小是2^该位置,但是不能超过end - start +1。

例如,start的二进制是x,那么最大的块大小是最大的2的幂次方,使得块大小 <= diff,并且块大小是start的二进制中最低位1所代表的值。

例如,假设start是192.168.0.5(转换为整数是3232235525),二进制是…00000101。最低位的1的位置是0(因为最后一位是1),所以块大小是1,即mask是32。这样该块只包含start自己。但如果diff大于1,那么可能之后的下一个块可以更大。

或者,这可能不是最优的方式。正确的做法应该是找到最大的mask,使得该子网覆盖的起始地址是start,并且子网的结束地址不超过end。

这可能需要计算该子网的网络地址,然后找到最大的mask,使得该子网的广播地址不超过end。

例如,子网的网络地址是start & mask,而广播地址是network | (~mask & 0xFFFFFFFFL)。所以需要确保广播地址 <= end。

但这个过程可能比较复杂。因此,可能需要另一种方法:

对于当前的start,尝试找到最大的mask,使得该mask对应的块大小(即2^(32 - mask))使得start + block_size -1 <= end.

同时,mask必须满足start的二进制中,后(32 - mask)位必须全为0,否则该子网的起始地址不是start,而是更小的地址。

例如,如果start是192.168.0.5,而mask是24,那么子网的起始地址是192.168.0.0,而start不在该子网的起始地址,因此无法使用mask为24。因此,必须找到最大的mask,使得start是该子网的起始地址,即start的后(32 - mask)位必须是0。

因此,正确的步骤应该是:

计算从start开始,最大的后缀0的位数。这可以通过计算start的最低位1的位置,比如start的二进制中有n个后缀0,那么最大的mask是32 - n。此时,块大小是2^n。例如,start是192.168.0.0(即…00000000),那么n是8,mask是24,块大小是256。

但如果start是192.168.0.128,二进制是…10000000,那么后缀0的位数是7,所以mask是25,块大小是128。此时,子网是192.168.0.128/25,广播地址是192.168.0.255。

但是,如果end是192.168.0.255,那么这个块可以包含。但是如果end是更小的值,比如192.168.0.200,那么需要调整mask,使得块大小不超过end - start +1。

因此,mask的确定应该是:
mask = 32 - Math.min(lowBit, log2(remainingSize))

其中,lowBit是start的最低位1的位置,remainingSize是end - start +1。

或者,另一种方法是:
mask = 32 - Math.min(lowBit, 32 - (int)(Math.log(remainingSize)/Math.log(2)))

但是,计算lowBit的方式需要正确。例如,对于start的二进制,找到最右边1的位置。比如,start的二进制是…0001000,那么lowBit是3(从0开始计数),即2^3=8。所以块的大小是8,mask是32 -3 =29。

3.2 如何计算lowBit呢?

可以用位运算来找最右边的1的位置。例如,对于long类型的start,取值为start & -start,这会得到一个只有最右边1的数,然后计算log2。

例如,start & -start可以得到一个数,其中只有最右边的1被保留。例如,start是5(二进制101),start & -start的结果是1(二进制1),所以log2(1)=0,即lowBit=0。此时,块的大小是1,mask=32-0=32.

如果是start是8(二进制1000),则start & -start=8log2(8)=3,所以lowBit=3,块的大小是8,mask=29.

所以,lowBit = log2(start & -start). 但如何计算log2?可以用位运算,或者循环右移。

在Java中,可以使用Long.numberOfTrailingZeros()方法,这个方法返回的是二进制中最低位1后面的0的个数。例如,对于8(1000),numberOfTrailingZeros是3。此时,lowBit就是这个值,即块的大小是1 << lowBit。

但这个方法是否适用于所有情况?

假设start是0,那么start & -start是0,这可能有问题。但start在这里是介于起始和结束之间的,所以可能不会为0?

但起始IP可能为0,例如0.0.0.0到0.0.0.255。此时,lowBit是8,块的大小是256,mask是24.

所以,lowBit的计算应该是:

long diff = start & -start;

如果diff ==0,那么lowBit=32?或者如何处理?

或者,另一种方式,使用Long.numberOfTrailingZeros(startIpLong)来获取最低位1后面的0的个数。例如,对于startIpLong=8(二进制1000),这个方法会返回3,因为末尾有3个0。此时,块的大小是1 << 3 =8.

所以,lowBit = Long.numberOfTrailingZeros(startIpLong | 0xFFFFFFFF00000000L)? 不太确定,可能需要仔细测试。

或者,对于IPv4的32位,startIpLong是32位的数值,存储在long类型的高32位或者低32位?可能要看转换方式是否正确。例如,前面的转换函数是否正确地将IP转换为长整数,比如将第一个八位移到高位。

例如,IP地址a.b.c.d转换为长整数的方式是:a<<24 | b<<16 | c<<8 | d。这样,IPv4的32位被放在long的高32位还是低32位?

假设转换正确的话,那么对于192.168.0.5,转换为长整数是 (192 <<24) | (168<<16) | (0<<8) |5 = 3232235525。二进制表示为:

11000000 10101000 00000000 00000101

这里,最低位的1是在第0位(最后一个字节的5是00000101,最低位是1)。所以,Long.numberOfTrailingZeros(3232235525)应该是0,因为最低位是1,后面没有0。所以块的大小是1,mask是32-0=32。

这样,每个块的大小是根据start的最低位1的位置决定的,但也要确保块的大小不超过剩余的地址空间(即end - start +1)。

3.3 综合起来,步骤如下

  1. startIpendIp转换为长整数s和e,确保s <=e。

  2. 初始化结果列表。

  3. s <= e时,循环:

a. 计算当前s的最低位1的位置,得到lowBit = Long.numberOfTrailingZeros(s).

b. 可能的块大小maxSize = 1 << lowBit.

c. 但maxSize不能超过e - s +1,所以实际块大小是Math.min(maxSize, e -s +1).

d. 这时候,需要找到最大的mask,使得块大小为actualSize。因为actualSize必须是2的幂,所以mask = 32 - log2(actualSize).

e. 所以,mask = 32 - (int)(Math.log(actualSize)/Math.log(2)).

f. 将CIDR块添加到结果列表:IP/mask.

g. s += actualSize.

这样,每次循环处理一个块,直到覆盖整个范围。

例如,start=192.168.0.5end=192.168.0.7:

s=3232235525 (0xC0A80005), e=3232235527.

第一次循环:

lowBit = trailing zeros of s=5(二进制101),即0位,所以maxSize=1<<0=1.

actualSize=min(1, 7-5+1=3) →1.

mask=32 -0=32.

CIDR块是192.168.0.5/32.

s +=1 →6.

第二次循环:

s=6,二进制00000110,trailing zeros是1(因为最后两位是 10 →最低位是0?)

不,Long.numberOfTrailingZeros(6)是1,因为6的二进制是110,末尾有一个0。

所以,lowBit=1 →maxSize=2.

actualSize=min(2, 7-6+1=2) →2.

mask=32 -1=31.

CIDR块是192.168.0.6/31,覆盖6和7.

s +=2 →8,超出e=7,循环结束。

所以,结果是两个CIDR块:/32和/31.

这似乎正确。

另一个例子,start=192.168.0.0end=192.168.3.255

s=3232235520, e=3232238079.

第一次循环:

lowBit=Long.numberOfTrailingZeros(s) →s=0xC0A80000 →二进制是11000000 10101000 00000000 00000000 →末尾有16个零?或者可能要看具体的转换方式。

如果IP地址转换为长整数的方式是每个八位作为高位,例如:

对于192.168.0.0,转换为:

192 <<24 | 168 <<16 | 0 <<8 |0 →即3232235520,二进制是11000000 10101000 00000000 00000000

Long.numberOfTrailingZeros(3232235520)→计算末尾的0的个数。3232235520的二进制是:

11000000 10101000 00000000 00000000 →最后的16位都是0,所以 trailing zeros是16.

所以,lowBit=16 →maxSize=1<<16=65536.

但实际剩余的大小是 e -s +1 = 3232238079 -3232235520 +1=2560.

所以actualSize=min(65536, 2560)=2560.

但2560不是2的幂次方。这时候需要调整,找到最大的2的幂次方,不超过2560.

所以,这里可能我的之前的思路有问题,即块的大小必须是2的幂次方,而实际剩余的大小可能不是,因此需要取最大可能的块,其大小是2的幂次方,并且不超过剩余的大小。

这时候,应该怎么做?

例如,在步骤c中,maxSize由lowBit决定,即可能的块大小是2^lowBit,但可能这个大小超过了剩余的大小,因此需要将actualSize设置为最大的2的幂次方,不超过剩余的大小。

或者,另一种方式,actualSize是最大的2的幂次方,使得actualSize <= remainingSize,并且actualSize <= maxSize(即由lowBit决定的块大小)。

例如,remainingSize是2560,那么最大的2的幂次方是2048(2^11=2048),而maxSize由lowBit=16是65536,所以actualSize=2048.

此时,mask=32 -11=21.

这样,CIDR块是192.168.0.0/21?但是这样的话,该块的范围是192.168.0.0到192.168.7.255,但用户的结束IP是192.168.3.255,因此这样会超出范围。这说明我的逻辑有问题。

哦,这说明原来的算法可能存在问题。可能需要重新考虑如何确定块的大小。

正确的逻辑应该是,找到最大的mask,使得该子网的起始地址是s,并且该子网的结束地址 <=e.

也就是说,子网的掩码决定了块的大小是2^(32 - mask),所以我们需要找到最大的mask(即最小的块大小),使得 s + (2^(32 - mask)) -1 <=e.

同时,该mask必须满足s的后(32 - mask)位是0,也就是s是该子网的起始地址。

例如,对于s=192.168.0.0e=192.168.3.255.

最大的mask是22,因为块大小是2(32-22)=210=1024,而s+1024-1=192.168.3.255。这正是需要的。

所以,正确的CIDR192.168.0.0/22.

因此,在这种情况下,算法应该返回一个CIDR块,而不是多个。那问题出在哪里?

块的大小应该同时满足:

  1. 该块的大小是2的幂次方,并且该块的起始地址是s。

  2. 该块的结束地址不超过e.

所以,块的大小应该是最大的可能的2的幂次方,满足 s + size -1 <=e,并且 size是最大的可能值,并且s mod size ==0.

3.4 如何计算这样的size?

算法步骤:

  • 计算remaining = e - s +1.

  • 初始size为最大的可能,即最大的2的幂次方 <= remaining.

  • 同时,size必须满足 s & (size -1) ==0 (即s是该size的倍数)。

如果这两个条件满足,则size可以是该值,否则,需要将size减半,直到满足条件。

例如,对于s=192.168.0.0,e=192.168.3.255,remaining=1024.

最大的size是1024,且s mod 1024 ==0,所以可以使用size=1024,mask=22.

所以,正确的CIDR是/22.

那如何将此逻辑转化为代码?

实现步骤:

  1. 计算remaining = e -s +1.

  2. 计算maxMask = 32 - (int)(Math.log(remaining)/Math.log(2))→但这可能不正确,因为remaining可能不是2的幂次方。

或者,找到最大的mask,使得 2^(32 - mask) <= remaining,并且 s & (2^(32 -mask) -1) ==0.

正确的算法应该是:

循环处理,每次找到最大的size(2的幂次方),使得:

size <= remaining

并且 s + size -1 <=e

并且 s & (size -1) ==0 (即s是size的整数倍)

然后,mask=32 - log2(size).

然后,将CIDR块加入列表,s += size,remaining = e -s +1.

重复直到remaining=0.

现在,如何在代码中实现这一点?

步骤:

while s <=e:

remaining = e -s +1;

maxSize = 1 << 30; // 初始为较大的数,比如2^30

size = 0;

for (int shift = 0; shift <=32; shift++) {
	currentSize = 1 << shift;
	if (currentSize > remaining) break;
	if ((s & (currentSize -1)) ==0) {
		size = currentSize;
	}
}

// 此时size是最大的满足条件的size

mask = 32 - log2(size) →即mask=32 - shift.

add cidr: ipToString(s) + "/" + mask

s += size

但这样可能需要一个循环来找到最大的size。

或者,可以使用位运算来找到最大的可能size:

size = remaining;

然后,找到最大的size,是2的幂次方,且满足 size <= remaining,并且 (s & (size -1)) ==0.

例如:

size = Integer.highestOneBit(remaining); // 最大的2的幂次方 <= remaining
while (size >0) {
	if ((s & (size -1)) ==0) {
		break;
	}
	size >>=1;
}

这样,size被调整为最大的满足条件的size.

例如,计算size的方法:

long size = Long.highestOneBit(remaining);

然后,循环检查 (s & (size -1)) ==0,否则将size减半,直到找到满足条件的size.

代码可能如下:

long remaining = e - s +1;
long size = Long.highestOneBit(remaining);
while (size >0) {
	if ((s & (size -1)) ==0) {
		break;
	}
	size >>=1;
}

if (size ==0) {
	size =1;
}

然后,mask=32 - log2(size). 但如何计算log2(size)?

可以用Long.numberOfLeadingZeros来得到。例如,对于size=1024,二进制是10000000000,共11位,所以numberOfLeadingZeros64-11=53。但Long.numberOfLeadingZeros(1024)返回的是64 - 11 =53?这可能需要调整。

或者,计算log2(size)可以使用位运算:

int logSize = 63 - Long.numberOfLeadingZeros(size);

例如,size=1024 →63 - 53(numberOfLeadingZeros(1024))10 →log2(1024)=10.

所以,mask=32 - logSize.

这样,例如size=1024 →mask=22.

所以,综合以上分析,Java代码的大致结构可以是:

public static List<String> ipRangeToCidrs(String startIp, String endIp) {

	// 转换startIp和endIp为long s和e
	List<String> cidrs = new ArrayList<>();
	long s = ipToLong(startIp);
	long e = ipToLong(endIp);
	while (s <= e) {
		// 计算remaining
		long remaining = e - s +1;
		
		// 计算最大的size
		long size = 1;
		int shift =0;
		
		// 找到最大的size=2^shift <= remaining,并且s & (size-1) ==0
		for (shift =0; (1L << (shift+1)) <= remaining; shift++) {
			if ((s & ( (1L << (shift+1)) -1 )) !=0) {
				break;
			}
		}
		
		size = 1L << shift;
		
		// 计算mask
		int mask = 32 - shift;
		
		// 添加CIDR
		cidrs.add(longToIp(s) + "/" + mask);
		
		// 更新s
		s += size;
	}
	
	return cidrs;
}

四、ipToLong函数:

public static long ipToLong(String ip) {

	String[] octets = ip.split("\\.");
	
	if (octets.length !=4) {
		throw new IllegalArgumentException("Invalid IP address");
	}
	
	long result =0;
	for (int i=0; i<4; i++) {
		int octet = Integer.parseInt(octets[i]);
		if (octet <0 || octet >255) {
			throw new IllegalArgumentException("Invalid IP octet");
		}
		result <<=8;
		result |= octet;
	}
	
	return result;
}

这样,对于192.168.0.5,结果是 (192 <<24) | (168 <<16) | (0 <<8) |5 = 3232235525L.

五、longToIp函数:

public static String longToIp(long ip) {
	return String.format("%d.%d.%d.%d",
	(ip >>24) & 0xFF,
	(ip >>16) & 0xFF,
	(ip >>8) & 0xFF,
	ip & 0xFF);
}

这样,3232235525L会被正确转换为192.168.0.5.

六、编写主要的循环部分

public static List<String> ipRangeToCidr(String startIp, String endIp) {
	long start = ipToLong(startIp);
	long end = ipToLong(endIp);
	List<String> result = new ArrayList<>();
	if (start > end) {
		throw new IllegalArgumentException("Start IP must be <= End IP");
	}
	while (start <= end) {
		// 计算当前start的最低位1的位置,确定最大的可能的mask
		long maxSize = 1;
		int shift =0;
		
		// 找到最大的shift,使得 (1 << shift) <= remaining,并且 (start & ((1 << shift) -1)) ==0
		long remaining = end - start +1;
		
		// 初始shift为0,逐步增加,直到无法满足条件
		for (shift =0; (1L << (shift+1)) <= remaining; shift++) {
		
			// 检查如果增加shift后,是否start的后面的位都是0
			long mask = (1L << (shift +1)) -1;
			if ((start & mask) !=0) {
				break;
			}
		}
		long currentSize = 1L << shift;
		int mask = 32 - shift;
		result.add(longToIp(start) + "/" + mask);
		start += currentSize;
	}
	return result;
}

综上所述,最终的Java函数应该包括:

  • ipToLonglongToIp的转换函数。

  • 主函数处理循环,找到每个可能的CIDR块。

  • 正确计算size和mask.

七、完整代码实现

以下是完整的Java 代码,并处理可能的异常和边界情况,可以将起始 IP 和结束 IP 转换为最精确的 CIDR 块列表。该方法通过位运算动态计算每个 CIDR 块的范围,确保覆盖所有 IP 且不重叠:

import java.util.ArrayList;
import java.util.List;

public class CidrCalculator {

    public static List<String> convertIpRangeToCidr(String startIp, String endIp) {
        long start = ipToLong(startIp);
        long end = ipToLong(endIp);
        List<String> cidrList = new ArrayList<>();

        if (start > end) {
            throw new IllegalArgumentException("起始IP不能大于结束IP");
        }

        while (start <= end) {
            // 计算当前起始IP的最低位有效位(决定CIDR块的大小)
            int suffixZeros = Long.numberOfTrailingZeros(start);
            long maxSize = 1L << suffixZeros;  // 可能的块大小(2^suffixZeros)
            long remaining = end - start + 1;  // 剩余需要覆盖的IP数量

            // 实际块大小不能超过剩余范围
            long actualSize = Math.min(maxSize, remaining);
            
            // 计算CIDR的掩码位数(32 - log2(actualSize))
            int mask = 32 - (int) (Math.log(actualSize) / Math.log(2));
            
            // 将CIDR块加入列表
            cidrList.add(longToIp(start) + "/" + mask);
            
            // 移动到下一个块的起始IP
            start += actualSize;
        }

        return cidrList;
    }

    // 将IP字符串转换为32位整数(用long存储)
    private static long ipToLong(String ip) {
        String[] octets = ip.split("\\.");
        if (octets.length != 4) {
            throw new IllegalArgumentException("无效的IP地址格式");
        }
        long result = 0;
        for (int i = 0; i < 4; i++) {
            int octet = Integer.parseInt(octets[i]);
            if (octet < 0 || octet > 255) {
                throw new IllegalArgumentException("IP八位组超出范围");
            }
            result = (result << 8) | octet;
        }
        return result;
    }

    // 将32位整数转换为IP字符串
    private static String longToIp(long ip) {
        return ((ip >> 24) & 0xFF) + "." +
               ((ip >> 16) & 0xFF) + "." +
               ((ip >> 8) & 0xFF) + "." +
               (ip & 0xFF);
    }

    // 测试用例
    public static void main(String[] args) {
        // 示例1:单个IP(/32)
        test("192.168.0.5", "192.168.0.5");  // ["192.168.0.5/32"]

        // 示例2:连续两个IP(/31)
        test("192.168.0.6", "192.168.0.7");  // ["192.168.0.6/31"]

        // 示例3:一个完整的C类子网(/24)
        test("192.168.0.0", "192.168.0.255");  // ["192.168.0.0/24"]

        // 示例4:跨子网的IP范围
        test("192.168.0.5", "192.168.0.7");  // ["192.168.0.5/32", "192.168.0.6/31"]
    }

    private static void test(String startIp, String endIp) {
        List<String> cidrs = convertIpRangeToCidr(startIp, endIp);
        System.out.println("IP范围: " + startIp + " - " + endIp);
        System.out.println("CIDR块: " + String.join(", ", cidrs) + "\n");
    }
}

7.1 代码说明

  1. 核心逻辑

    • 位运算计算:通过 Long.numberOfTrailingZeros 找到起始 IP 的最低位有效位,确定最大可能的 CIDR 块大小。
    • 动态调整块大小:根据剩余 IP 范围 (remaining) 和起始 IP 的对齐要求,动态调整实际 CIDR 块大小。
  2. 关键方法

    • ipToLong():将 IP 字符串转换为 32 位整数(用 long 存储,避免符号问题)。
    • longToIp():将整数转换回点分十进制格式。
    • convertIpRangeToCidr():主方法,遍历 IP 范围生成 CIDR 列表。
  3. 测试案例

    • 覆盖单 IP、连续 IP、完整子网和跨子网范围,验证不同场景的准确性。

7.2 执行示例

输入:

convertIpRangeToCidr("192.168.0.6", "192.168.0.7");

输出:

CIDR块: 192.168.0.6/31

输入:

convertIpRangeToCidr("192.168.0.5", "192.168.0.7");

输出:

CIDR块: 192.168.0.5/32, 192.168.0.6/31

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2308045.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[STM32]从零开始的STM32 DEBUG问题讲解及解决办法

一、前言 最近也是重装了一次keil&#xff0c;想着也是重装了&#xff0c;也是去官网下载了一个5.41的最新版&#xff0c;在安装和配置编译器和别的版本keil都没太大的区别&#xff0c;但是在调试时&#xff0c;遇到问题了&#xff0c;在我Debug的System Viewer窗口中没有GPIO&…

MySQL当中的Lock

1. 总览锁的类型 锁的类型&#xff1a; 锁类型 符号/缩写 描述 全局锁 FTWRL 锁定整个数据库&#xff08;FLUSH TABLES WITH READ LOCK&#xff09;&#xff0c;用于全库备份。 表级锁 - 表锁 S/X LOCK TABLES ... READ&#xff08;共享锁&#xff09;或 WRITE&#…

electron-builder打包时github包下载失败【解决办法】

各位朋友们&#xff0c;在使用electron开发时&#xff0c;选择了electron-builder作为编译打包工具时&#xff0c;是否经常遇到无法从github上下载依赖包问题&#xff0c;如下报错&#xff1a; Get "https://github.com/electron/electron/releases/download/v6.1.12/ele…

【免费】YOLO[笑容]目标检测全过程(yolo环境配置+labelimg数据集标注+目标检测训练测试)

一、yolo环境配置 这篇帖子是我试过的&#xff0c;非常全&#xff0c;很详细【cudaanacondapytorchyolo(ultralytics)】 yolo环境配置 二、labelimg数据集标注 可以参考下面的帖子&#xff0c;不过可能会出现闪退的问题&#xff0c;安装我的流程来吧 2.1 labelimg安装 label…

服务器IPMI用户名、密码批量检查

背景 大规模服务器部署的时候&#xff0c;少不了较多的网管和监测平台&#xff0c;这些平台会去监控服务器的性能、硬件等指标参数&#xff0c;为了便于管理和控制&#xff0c;则需要给服务器IPMI带外管理添加较多的用户&#xff0c;这就需要对较多的服务器检查所对应的IPMI用…

小红书湖仓架构的跃迁之路

作者&#xff1a;李鹏霖(丁典)&#xff0c;小红书-研发工程师&#xff0c;StarRocks Contributor & Apache Impala Committer 本文整理自小红书工程师在 StarRocks 年度峰会上的分享&#xff0c;介绍了小红书自助分析平台中&#xff0c;StarRocks 与 Iceberg 结合后&#x…

C++-第十七章:包装器

目录 第一节&#xff1a;std::function 第二节&#xff1a;std::bind 2-1.基本介绍 2-2.调整顺序(不常用) 2-3.调整个数 2-4.std::bind与std::function 下期预告&#xff1a; C中有3种可调用对象&#xff1a;函数指针、仿函数对象、lambda函数&#xff0c;经过包装器包装后屏…

TCP的三次握手与四次挥手:建立与终止连接的关键步骤

引言 ‌TCP&#xff08;传输控制协议&#xff09;工作在OSI模型的传输层‌。OSI模型将计算机网络功能划分为七个层级&#xff0c;从底层到顶层依次是&#xff1a;物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。传输层负责在网络节点之间提供可靠的端到端通信&a…

2025计算机考研复试资料(附:网课+历年复试真题+140所高校真题+机试)

目录 2025 计算机考研复试经验全攻略&#xff0c;附超全资源&#x1f381; &#xff08;一&#xff09;网课资源 &#xff08;二&#xff09;历年复试真题 &#xff08;三&#xff09;140 所高校真题 二、专业知识复习篇 &#xff08;一&#xff09;复试专业课程 二&…

Milvus高性能向量数据库与大模型结合

Milvus | 高性能向量数据库&#xff0c;为规模而构建Milvus 是一个为 GenAI 应用构建的开源向量数据库。使用 pip 安装&#xff0c;执行高速搜索&#xff0c;并扩展到数十亿个向量。https://milvus.io/zh Milvus 是什么&#xff1f; Milvus 是一种高性能、高扩展性的向量数据…

腾讯游戏完成架构调整 IEG新设五大产品事业部

易采游戏网2月28日独家消息&#xff1a;继1月份腾讯天美工作室群完成内部组织架构调整后&#xff0c;腾讯旗下互动娱乐事业群&#xff08;IEG&#xff09;再次宣布对组织架构进行优化调整。此次调整的核心在于新设立了五大产品事业部&#xff0c;包括体育产品部、音舞产品部、V…

达梦数据库系列之安装及Mysql数据迁移

达梦数据库系列之安装及Mysql数据迁移 1. 达梦数据库1.1 简介1.2 Docker安装达梦1.2.1 默认密码查询1.2.2 docker启动指定密码 1.3 达梦数据库连接工具1.3.1 快捷键 2 Mysql数据库迁移至达梦2.1 使用SQLark进行数据迁移 1. 达梦数据库 1.1 简介 DM8是达梦公司在总结DM系列产品…

java jar包内的jar包如何打补丁

问题描述&#xff1a; 主包&#xff1a;hisca.jar&#xff0c;解压后 BOOT-INFO/lib下有其他jar包 因为一个小bug&#xff0c;需要修改这个hisca包下BOOT-INF/lib下的子jar包service-hisca-impl-1.0.0.jar中的一个service类及xml文件 操作步骤&#xff1a; 1、主包jar -xvf …

【企业场景】上线的项目如何进行限流

一、常见的四种速率限流算法 对于限流&#xff0c;最为直接的就是速率限流了 固定窗口算法 比如 10r/s 就是把时间线分为 1s 一段&#xff0c;也就是周期为 1s&#xff0c;对一个时间段的请求进行计数&#xff0c;超过 10 则舍弃&#xff0c;未超过则直接处理经过 1s 后&…

git - study

文章目录 git - study概述可以用 git gui工具来添加快捷命令工具如果要在提交日志中搜索&#xff0c;可以用gitk的view编辑功能实验环境直接用git自带环境进行git操作的好处查看git所有配置配置全局数据配置项目专用的数据查询配置数据的原始值配置git使用的文本编辑器获取某个…

FPGA之硬件设计笔记-持续更新中

目录 1、说在前面2、FPGA硬件设计总计说明3、 原理图详解 - ARITX - 7 系列3.1 顶层框图介绍3.2 FPGA 电源sheet介绍&#xff1a;3.2.1 bank 14 和 bank 15的供电3.2.2 bank 0的供电3.2.3 Bank34 35 的供电 3.3 核电压和RAM电压以及辅助电压 4 原理图详解-- Ultrascale ARTIX4.…

嵌入式开发:傅里叶变换(5):STM32和Matlab联调验证FFT

目录 1. MATLAB获取 STM32 的原始数据 2. 将数据上传到电脑 3. MATLAB 接收数据并验证 STM32进行傅里叶代码 结果分析 STM32 和 MATLAB 联调是嵌入式开发中常见的工作流程&#xff0c;通常目的是将 STM32 采集的数据或控制信号传输到 MATLAB 中进行实时处理、分析和可视化…

Cursor AI编程-详细教程

一点准备工作 Cursor方法论&#xff1a;简单到没有方法 Cursor能做什么 Cursor官网&#xff1a;https://www.cursor.com/ja Cursor文档&#xff1a;Cursor – Welcome to Cursor Cursor论坛&#xff1a;Weekly - Cursor - Community Forum 写程序代码 举例&#xff1a; 设…

(十 三)趣学设计模式 之 模版方法模式!

目录 一、 啥是模板方法模式&#xff1f;二、 为什么要用模板方法模式&#xff1f;三、 模板方法模式的实现方式四、 模板方法模式的优缺点五、 模板方法模式的应用场景六、 总结 &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博主的讲解方式&a…

泛型编程、函数模板、类模板

目录 一、泛型编程 1.泛型编程提出背景 1.1.代码复用案例解析 案例1&#xff1a;实现一个交换函数&#xff0c;并对不同类型参数进行函数重载 (1)调试 (2)代码解析 ①代码复用问题 ②泛型编程的解决方案 ③上面泛型Swap函数模版的优点 1.2.泛型编程提出背景 2.泛型编…