net.IP.String() 返回空字符串因底层为nil或长度非法;ParseIP()静默返回nil需判空;ParseCIDR()后用Contains()判断IP归属,注意归一化;Mask.Size()获取真实前缀长度。

net.IP.String() 为什么有时返回空字符串或奇怪格式
因为 net.IP 是字节切片别名,底层可能为 nil 或长度不合法(IPv4 非 4 字节、IPv6 非 16 字节),调用 String() 会直接返回空字符串,而不是 panic。常见于未校验的用户输入或解析失败后继续使用。
- 始终在调用
String()前检查ip != nil - IPv4 地址建议用
ip.To4()归一化后再转字符串,避免::ffff:192.0.2.1这类 IPv4-mapped IPv6 格式 - 若需固定格式输出(如总显示点分十进制),优先用
ip.To4().String(),To4()对纯 IPv6 返回 nil,可据此区分类型
ParseIP 失败但没报错?——常见解析盲区
net.ParseIP() 不会返回 error,而是静默返回 nil。这是最常被忽略的设计点:它只做格式识别,不校验语义合法性(比如 256.1.1.1 会被截断为 0.1.1.1)。
- 必须显式判空:
if ip == nil { /* 解析失败 */ } - 需要严格校验时,先用
ParseIP(),再用ip.To4() != nil || ip.To16() != nil确认是否为合法地址 - 对 CIDR 输入(如
"192.168.1.0/24"),不能直接传给ParseIP()—— 它只处理纯 IP 字符串,得用net.ParseCIDR()
net.ParseCIDR() 解析后怎么判断是否包含某个 IP
net.ParseCIDR() 返回 *net.IPNet,其 Contains() 方法才是判断归属的正确方式。别手动比对掩码位,容易出错且不兼容 IPv6。
-
_, ipnet, _ := net.ParseCIDR("10.0.0.0/8");然后ipnet.Contains(net.ParseIP("10.5.6.7")) - 注意:传入
Contains()的 IP 必须是归一化后的,比如 IPv4-mapped IPv6(::ffff:127.0.0.1)不会被 IPv4 网段匹配,需先转成 IPv4:ip.To4() - 如果 IP 来自用户输入,务必先
ParseIP()再判空,否则Contains(nil)会 panic
IPv4 和 IPv6 的 net.IPNet.Mask 区别与陷阱
ipnet.Mask 是 net.IPMask 类型,本质是字节切片。IPv4 掩码长度固定为 4 字节,IPv6 为 16 字节 —— 但 Mask.Size() 才是真实前缀长度,别直接 len(Mask)。
立即学习“go语言免费学习笔记(深入)”;
- 获取前缀长度统一用
ones, bits := ipnet.Mask.Size(),bits总是 32 或 128,ones是实际掩码位数(如 /24 → 24) - 手动构造掩码时,不要用
net.CIDRMask(ones, bits)硬编码bits值,而应根据 IP 类型动态选:IPv4 用 32,IPv6 用 128 - 比较两个网段是否相同,别比
Mask字节切片,要用ipnet1.String() == ipnet2.String()或分别比IP和Mask.Size()
事情说清了就结束










