Linux 下判断一个字符串ip是否合法

前言

要判断一个IP地址是否合法,可以使用C语言标准库中的inet_pton()函数。

inet_pton() 函数是一个标准的C库函数,用于将一个IPv4或IPv6地址的字符串表示转换为网络字节序的二进制值。

inet_pton()函数可以根据传入的地址族(Address Family)参数,将一个字符串表示的IP地址转换为相应的二进制形式。如果转换成功,函数返回1,则表示该字符串ip地址合法。

一、inet_pton函数简介

NAME
       inet_pton - convert IPv4 and IPv6 addresses from text to binary form

SYNOPSIS
       #include <arpa/inet.h>

       int inet_pton(int af, const char *src, void *dst);

此函数将字符串src转换为af地址族中的网络地址结构,然后将网络地址结构复制到dst。af参数必须是AF_INET(IPv4)或AF_INET6(IPv6)。
当前支持以下地址族:
(1)AF_INET
src指向一个字符串,该字符串包含十进制点格式的IPv4网络地址“ddd.ddd.ddd.ddd”,其中ddd是0到255范围内最多三位的十进制数。地址被转换为 struct in_addr 并复制到dst,dst的大小必须为(struct in_addr)(4)字节(32位)长。

/* Internet address.  */
typedef uint32_t in_addr_t;
struct in_addr
  {
    in_addr_t s_addr;
  };

(2)AF_INET6
src指向一个包含IPv6网络地址的字符串。地址被转换为 struct in6_addr 并复制到dst,dst的大小必须为(struct in6_addr)(16)字节(128位)长。

/* IPv6 address */
struct in6_addr
  {
    union
      {
	uint8_t	__u6_addr8[16];
	uint16_t __u6_addr16[8];
	uint32_t __u6_addr32[4];
      } __in6_u;
#define s6_addr			__in6_u.__u6_addr8
#ifdef __USE_MISC
# define s6_addr16		__in6_u.__u6_addr16
# define s6_addr32		__in6_u.__u6_addr32
#endif
  };

允许的IPv6地址格式遵循以下规则:
1.首选格式为:

 x:x:x:x:x:x:x:x   //一个x表示一个十六进制数,即两个字节

这种形式由八个十六进制数组成,每个十六进制数表示一个16位的值。

2.首选格式的一系列连续零值可以缩写为 ::。一个地址中只能出现 :: 的一个实例。例如,环回地址0:0:0:0:0:0:1可以缩写为 ::1。通配符地址由全零组成,可以写成 ::。

3.另一种格式可用于表示IPv4映射的IPv6地址。这种形式写为:

x:x:x:x:x:x:d.d.d.d

其中六个前导xs是十六进制值,定义地址的六个最高有效的16位(即96位),而ds以点十进制表示法表示一个值,该值定义地址的最低有效32位。这样一个地址的例子是:

::FFFF:204.152.189.116

inet_pton()在成功时返回1(网络地址已成功转换)。如果src不包含表示指定地址族中有效网络地址的字符串,则返回0。如果af不包含有效的地址族,则返回-1,并将errno设置为EAFNOSUPPORT。

二、使用例子

#include <stdio.h>
#include <arpa/inet.h>

int main(int argc, char **argv) {

    const char* ip_addr_str = argv[1];

    struct in_addr ipv4_addr;
    struct in6_addr ipv6_addr;

    if (inet_pton(AF_INET, ip_addr_str, &ipv4_addr) == 1) {
        printf("%s is an IPv4 address.\n", ip_addr_str);
    } else if (inet_pton(AF_INET6, ip_addr_str, &ipv6_addr) == 1) {
        printf("%s is an IPv6 address.\n", ip_addr_str);
    } else {
        printf("Failed to convert %s to binary representation.the ip_str is not vaild address\n", ip_addr_str);
    }

    return 0;
}

如果要判断一个IP地址是否合法,只需要调用inet_pton()函数,并检查其返回值即可。如果返回值为1,说明IP地址合法,否则说明IP地址不合法。

上述代码说明首先定义了一个IP地址字符串,然后创建了一个 struct in_addr 类型的变量 ipv4_addr,用于存储IPv4地址的二进制值,以及一个 struct in6_addr 类型的变量 ipv6_addr,用于存储IPv6地址的二进制值。

接下来,我们先调用 inet_pton() 函数,将IP地址字符串转换为IPv4地址的二进制值。如果转换成功,则说明该字符串表示的是IPv4地址,否则我们再调用 inet_pton() 函数,将IP地址字符串转换为IPv6地址的二进制值。如果转换成功,则说明该字符串表示的是IPv6地址,否则说明IP地址字符串格式不正确或者不属于IPv4或IPv6地址。即该字符串Ip地址不合法。

三、其他例子

要检查一个字符串是否表示一个合法的IP地址,可以使用正则表达式来验证。

下面是使用 python 和 c++11 来检查一个字符串ipv4的地址是否正确。

3.1 python

import sys
import re

def is_valid_ipv4(ip: str) -> bool:
    pattern = r'^(\d{1,3}\.){3}\d{1,3}$'
    if re.match(pattern, ip):
        octets = ip.split('.')
        if all(0 <= int(octet) <= 255 for octet in octets):
            return True
    return False

str_ip = sys.argv[1]

if __name__ == '__main__':
    ret = is_valid_ipv4(str_ip)
    if ret == True:
        print('the strip {0} is vaild'.format(str_ip))
        
    else:
        print('the strip {0} is not vaild'.format(str_ip))
   
$ python3 ipv4.py 1.1.1.1
the strip 1.1.1.1 is vaild

(1)定义IPv4地址的正则表达式,使用了分组和重复匹配来匹配四个数字(0-255)组成的IP地址。
(2)使用 re.match() 函数来执行正则表达式匹配,如果匹配成功,返回一个 Match 对象;否则返回 None。
(3)如果匹配成功,使用 split() 函数将IP地址字符串分割为四个数字,并使用 all() 函数来检查每个数字是否在0-255之间。
(4)如果所有数字都在0-255之间,则返回 True,表示IP地址合法;否则返回 False,表示IP地址不合法。

其中:

 pattern = r'^(\d{1,3}\.){3}\d{1,3}$'

这是一个正则表达式,用于匹配IPv4地址的字符串。下面是该正则表达式的各个部分的含义解释:

^:匹配字符串的开头。
(:开始一个分组。
\d{1,3}:匹配13位数字。
.:匹配一个字符(点号)。
):结束分组。
{3}:重复匹配前面的分组3次,即匹配三段由数字和点号组成的字符串。
\d{1,3}:匹配13位数字。
$:匹配字符串的结尾。

因此,该正则表达式匹配的字符串应该由四个数字段组成,每个数字段由1到3个数字组成,数字段之间用点号分隔。例如,符合该正则表达式的字符串包括 127.0.0.1、192.168.1.100 等,而不符合该正则表达式的字符串包括 127.0.0.256、192.168.1 等。

在 Python 中,使用 r 前缀可以将一个字符串标记为“原始字符串”,即字符串中的特殊字符(如反斜杠)不会被解释为转义字符。因此,r’^(\d{1,3}.){3}\d{1,3}$’ 表示一个原始字符串,其中的 \d 表示匹配任意数字字符,而 . 表示匹配任意字符(除了换行符)。

3.2 c++11

#include <iostream>
#include <regex>
#include <string>

bool is_valid_ip(std::string ip_str) {
    // 定义IPv4地址正则表达式
    //std::regex ipv4_regex("^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$");
    std::regex ipv4_regex("^(([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.){3}([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$");

    // 执行正则表达式匹配
    bool match_result = std::regex_match(ip_str, ipv4_regex);

    // 如果匹配成功,返回true;否则返回false
    return match_result;
}

int main() {
    std::string ip_str;
    std::cout << "请输入IP地址:";
    std::cin >> ip_str;

    if (is_valid_ip(ip_str)) {
        std::cout << ip_str << " 是合法的IPv4地址" << std::endl;
    } else {
        std::cout << ip_str << " 不是合法的IPv4地址" << std::endl;
    }

    return 0;
}

C++11 引入了正则表达式库 std::regex,可以方便地使用正则表达式来匹配字符串。

其中:

std::regex ipv4_regex("^(([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.){3}([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$");

这是一个正则表达式,用于匹配IPv4地址的字符串:

^:匹配字符串的开头。
(:开始一个分组。
(:开始一个分组。
[01]?[0-9]?[0-9]:匹配03位数字,可以是01开头的数字(如010203等),也可以是普通的09的数字。
|:表示或者。
2[0-4][0-9]:匹配200249之间的数字。
|:表示或者。
25[0-5]:匹配250255之间的数字。
):结束分组。
\\.:匹配一个点号字符(注意需要转义)。
):结束分组。
{3}:重复匹配前面的分组3次,即匹配三段由数字和点号组成的字符串。
[01]?[0-9]?[0-9]:匹配03位数字,可以是01开头的数字(如010203等),也可以是普通的09的数字。
|:表示或者。
2[0-4][0-9]:匹配200249之间的数字。
|:表示或者。
25[0-5]:匹配250255之间的数字。
$:匹配字符串的结尾。

因此,整个正则表达式的含义是:匹配由四段数字和点号组成的字符串,每一段数字的范围为0到255,且字符串的开头和结尾不能有其他字符。这符合 IPv4 地址的格式规范。

也可以这么写:

std::regex ipv4_regex("^((\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])$");
^:匹配字符串的开头。
(:开始一个分组。
\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]:匹配一个数字段,可以是0-255之间的数字。这里的 \\d{1,2} 表示匹配 12 位数字,1\\d{2} 表示匹配 100199 之间的数字,2[0-4]\\d 表示匹配 200249 之间的数字,25[0-5] 表示匹配 250255 之间的数字。
\\.:匹配一个点号字符(需要转义)。
):结束分组。
{3}:重复匹配前面的分组 3 次,即匹配三段由数字和点号组成的字符串。
(\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]):匹配第四段数字,可以是 0-255 之间的数字。
$:匹配字符串的结尾。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值