C语言编程实现对IPV4地址的合法性判断(使用正则表达式)
有了解过我的朋友,可能有点印象,我在N年前的博客中,就写了这个主题,当时确实是工作中遇到了这个问题。本想着等工作搞完之后,就把这个问题的解决代码补上,结果一鸽,就是好几年,真是惭愧。现在把这部分代码公开,欢迎大家来下载测试。
文章目录
- 1 写在前面
- 2 什么是正则表达式
- 3 需求分析
- 4 C语言版本(正则表达式)
- 5 完整测试用例
1 写在前面
之前我写过一篇博文,主要介绍如何使用C语言编程实现对IPV4地址的合法性进行判断,不过那次我使用的原生的C语言函数来实现的,本篇博文将给大家介绍一下,如何在C语言编程的环境下,使用正则表达式完成这个功能需求。
2 什么是正则表达式
正则表达式是一种用于匹配文本模式的工具,它可以通过一些特定的字符和语法规则来描述一个文本模式,并在文本中查找符合该模式的字符串。正则表达式可以用于文本搜索、替换、验证等多种应用场景,是程序员和文本处理工作者必备的工具之一。
正则表达式,在一些高级编程语言中,都有成熟的库接口来调用,而在C语言中,却鲜有这样的例子。但并不是说它不能用,其实它也是可以用的。
在C语言中使用正则表达式时,有一些注意事项需要注意:
- 首先需要包含
regex.h
头文件。 - 在使用正则表达式之前,需要使用
regcomp()
函数将正则表达式编译成一个模式。 - 在使用
regexec()
函数执行正则表达式时,可以使用REG_EXTENDED
选项来启用扩展正则表达式,或使用REG_ICASE
选项来忽略大小写。 - 在使用
regexec()
函数时,如果返回值为0,表示匹配成功;如果返回值为REG_NOMATCH
,表示没有匹配成功;如果返回值为其他值,表示发生了错误。 - 在使用
regerror()
函数获取错误信息时,需要提供一个缓冲区和缓冲区大小。 - 在使用完正则表达式后,需要使用
regfree()
函数释放编译后的模式。 - 正则表达式中的特殊字符需要进行转义,例如
.
需要写成\.
,否则它将匹配任何字符。 - 正则表达式中的括号可以用于分组,例如
([0-9]{1,3}\.){3}[0-9]{1,3}
可以匹配一个IP地址。 - 在使用正则表达式时,需要注意性能问题,因为正则表达式匹配可能会消耗大量的CPU资源。可以考虑使用更简单的字符串匹配算法,例如
strstr()
函数。
更多关于正则表达式的介绍,可以参考:正则表达式语言 - 快速参考 | Microsoft Learn
3 需求分析
其实,本专题的需求很简单,就是输入一段字符串,判断它是不是合法的IPv4地址。仅仅从功能上看,似乎很简单,但是真正要做到很完美,也是需要下点功夫的。不信,你看看下文的拆解。
4 C语言版本(正则表达式)
我们先上一个简单版本,直接看代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <regex.h>
static int is_valid_ipv4_regex(const char *ip_address)
{
regex_t regex;
int reti;
// Compile regular expression
reti = regcomp(®ex, "^([0-9]{1,3}\\.){3}[0-9]{1,3}$", REG_EXTENDED);
if (reti) {
fprintf(stderr, "Could not compile regex\n");
reti = 0;
}
// Execute regular expression
printf("%s\n", ip_address);
reti = regexec(®ex, ip_address, 0, NULL, 0);
if (!reti) {
printf("Valid IP address %d\n", reti);
reti = 1;
} else if (reti == REG_NOMATCH) {
printf("Invalid IP address xxx %d\n", reti);
reti = 0;
} else {
char error_message[100];
regerror(reti, ®ex, error_message, sizeof(error_message));
fprintf(stderr, "Regex match failed: %s\n", error_message);
reti = 0;
}
exit_entry:
// Free compiled regular expression
regfree(®ex);
return reti;
}
int is_valid_ipv4(const char *ip_address)
{
int num, dots = 0;
char *ptr;
if (ip_address == NULL) {
return 0;
}
ptr = strtok((char *)ip_address, ".");
if (ptr == NULL) {
return 0;
}
while (ptr) {
if (!isdigit(*ptr)) {
return 0;
}
if (*ptr == '0') { //check start '0'
return 0;
}
num = atoi(ptr);
if (num < 0 || num > 255) {
return 0;
}
ptr = strtok(NULL, ".");
if (ptr != NULL) {
dots++;
}
}
if (dots != 3) {
return 0;
}
if (atoi(ip_address) >= 1 && atoi(ip_address) <= 126) {
printf("This is a Class A IP address.\n");
return 1;
} else if (atoi(ip_address) >= 128 && atoi(ip_address) <= 191) {
printf("This is a Class B IP address.\n");
return 1;
} else if (atoi(ip_address) >= 192 && atoi(ip_address) <= 223) {
printf("This is a Class C IP address.\n");
return 1;
} else {
printf("This is not a Class A, B, or C IP address.\n");
return 0;
}
return 1;
}
int check_is_valid_ipv4(const char *ip)
{
int ret = 0;
//ret = is_valid_ipv4(ip);
ret = is_valid_ipv4_regex(ip);
return ret;
}
int main(int argc, const char *argv[])
{
const char *ip = argv[1];
printf("check %s\n", ip);
printf("ret %d\n", check_is_valid_ipv4(ip));
}
编译运行一下,输入一个常见的ipv4地址是没有问题,比如 “192.168.0.1”;同时,非法的字符输入也是会报错的。
recan@ubuntu:~$
recan@ubuntu:~$
recan@ubuntu:~$ ./test 192.168.1.3
check 192.168.1.3
192.168.1.3
Valid IP address 0
ret 1
recan@ubuntu:~$ ./test 192.168.1.t
check 192.168.1.t
192.168.1.t
Invalid IP address xxx 1
ret 0
recan@ubuntu:~$
recan@ubuntu:~$ ./test 192.168.1.oo
check 192.168.1.oo
192.168.1.oo
Invalid IP address xxx 1
ret 0
recan@ubuntu:~$
recan@ubuntu:~$ ./test 192.168.01.8
check 192.168.01.8
192.168.01.8
Valid IP address 0
ret 1
recan@ubuntu:~$
recan@ubuntu:~$
但是细心的朋友可能互发现,当IP地址某一段带前导0时,貌似也会判断为正确的IP地址,而实际上我们一般不会这样写。
那么如何才能规避掉这种情况呢?
有没有可能通过正则表达式来完成呢?
这个问题留给读者自行去探索,是个非常有趣的正则表达式学习。
只要完成了这个功能,相信一定能够让你对正则表达式掌握得更加深入。
5 完整测试用例
本小节给大家补充一下各种测试用例,希望对大家测试代码有帮助:
合法的测试输入
192.168.0.1
10.0.0.1
172.16.0.1
255.255.255.255
非法的测试输入
256.0.0.1
192.168.0.0.1
192.168.0
192.168.0.1.2
非法的测试输入
256.0.0.1
192.168.0.0.1
192.168.0
192.168.0.1.2
300.300.300.300
1.2.3
1.2.3.4.5
1.2.3.4.
.1.2.3.4
1..2.3.4
测试用例是不断丰富的,欢迎大家来补充。