**C語言編程實現(xiàn)對IPV4地址的合法性判斷**
> 有了解過我的朋友,可能有點印象,我在N年前的博客中,就寫了這個主題,當時確實是工作中遇到了這個問題。本想著等工作搞完之后,就把這個問題的解決代碼補上,結果一鴿,就是好幾年,真是慚愧。現(xiàn)在把這部分代碼公開,歡迎大家來下載測試。
@[toc]
# 1 寫在前面
有了解過我的朋友,可能有點印象,我在N年前的博客中,就寫了這個主題,當時確實是工作中遇到了這個問題。本想著等工作搞完之后,就把這個問題的解決代碼補上,結果一鴿,就是好幾年,真是慚愧。現(xiàn)在把這部分代碼公開,歡迎大家來下載測試。
如果你發(fā)現(xiàn)代碼有問題,歡迎與我私信聯(lián)系。
# 2 需求分析
其實,本專題的需求很簡單,就是輸入一段字符串,判斷它是不是合法的IPv4地址。僅僅從功能上看,似乎很簡單,但是真正要做到很完美,也是需要下點功夫的。不信,你看看下文的拆解。
![IPV4 的圖像結果](https://img-blog.csdnimg.cn/img_convert/994e3109304e34a6bbd59db8247d37c6.jpeg)
# 3 簡單版本
我們先上一個簡單版本,直接看代碼:
```c
#include
#include
#include
#include
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;
}
num = atoi(ptr);
if (num < 0 || num > 255) {
return 0;
}
ptr = strtok(NULL, ".");
if (ptr != NULL) {
dots++;
}
}
if (dots != 3) {
return 0;
}
return 1;
}
int check_is_valid_ipv4(const char *ip)
{
int ret = 0;
ret = is_valid_ipv4(ip);
return ret;
}
int main(int argc, const char *argv[])
{
const char *ip = argv[1];
printf("check %sn", ip);
printf("ret %dn", check_is_valid_ipv4(ip));
}
```
編譯運行一下,輸入一個常見的ipv4地址是沒有問題,比如 "192.168.0.1";同時,非法的字符輸入也是會報錯的。
```c
~/ipv4]$gcc -o test ipv4.c
~/ipv4]$./test 192.168.0.1
check 192.168.0.1
ret 1
~/ipv4]$./test
check 192.168.w.2
ret 0
```
但是,如果我加一個限制:如何判斷一個IPV4地址是一個合法的 **主機地址** 呢?
比如這個:“238.171.84.41”,它的判斷還是合法的哦,實際上這不是合法的 **主機地址** 。
```c
~/ipv4]$./test 238.171.84.41
check 238.171.84.41
ret 1
```
另外一個,上面的代碼還檢測不到,諸如此類的輸入:“0192.168.1.1”
```c
~/ipv4]$./test 0192.168.1.1
check 0192.168.1.1
ret 1
```
所以,我們需要優(yōu)化一下。
# 4 進階一下
正如上面的分析,我們需要對代碼進行優(yōu)化:
首先得判斷按照 "." 分割后的數(shù)字段,不能以 "0" 字符開頭。
```c
while (ptr) {
if (!isdigit(*ptr)) {
return 0;
}
if (*ptr == '0') { //check start with '0'
return 0;
}
num = atoi(ptr);
if (num < 0 || num > 255) {
return 0;
}
ptr = strtok(NULL, ".");
if (ptr != NULL) {
dots++;
}
}
~/ipv4]$./test 0192.168.1.1
check 0192.168.1.1
ret 0
```
根據(jù)IPv4地址的分類:
- A類:(1.0.0.1-126.255.255.254)(默認子網(wǎng)掩碼:255.0.0.0或0xFF000000)第一個字節(jié)為網(wǎng)絡號,后三個字節(jié)為主機號,表示為網(wǎng)絡--主機--主機--主機。該類IP地址的最前面為“0”,所以地址的網(wǎng)絡號取值于1~126之間。共有16777214個主機地址,一般用于大型網(wǎng)絡。
- B類:(128.1.0.1-191.254.255.254)(默認子網(wǎng)掩碼:255.255.0.0或0xFFFF0000)前兩個字節(jié)為網(wǎng)絡號,后兩個字節(jié)為主機號。該類IP地址的最前面為“10”,所以地址的網(wǎng)絡號取值于128~191之間。共有65534個主機地址,一般用于中等規(guī)模網(wǎng)絡。
- C類:(192.0.1.1-223.255.254.254)(子網(wǎng)掩碼:255.255.255.0或0xFFFFFF00)前三個字節(jié)為網(wǎng)絡號,最后一個字節(jié)為主機號。該類IP地址的最前面為“110”,所以地址的網(wǎng)絡號取值于192~223之間。共有254個主機地址,一般用于小型網(wǎng)絡。
- D類:是多播地址。(224.0.0.1-239.255.255.254) 該類IP地址的前面4位為“1110”,所以網(wǎng)絡號取值于224~239之間;后面28位為組播地址ID。這是一個專門保留的地址。它并不指向特定的網(wǎng)絡,目前這一類地址被用在多點廣播(Multicasting)中。多點廣播地址用來一次尋址一組計算機,它標識共享同一協(xié)議的一組計算機。
- E類:是保留地址,為將來使用保留。(240.0.0.0---255.255.255.254) 該類IP地址的最前面為“1111”,所以網(wǎng)絡號取值于240~255之間。
可知,如果要符合一個正常的IPv4主機地址,只能是A、B、C類,而不能是D、E類。
所以在判斷時,我們應該增加IPv4地址的類別判斷。
在上面的判斷返回前,增加一個判斷:
```c
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;
}
```
完整的代碼如下:
```c
#include
#include
#include
#include
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);
return ret;
}
int main(int argc, const char *argv[])
{
const char *ip = argv[1];
printf("check %sn", ip);
printf("ret %dn", check_is_valid_ipv4(ip));
}
```
這個時候,我們再試一下之前的非A/B/C類的IPv4地址:
```c
~/ipv4]$./test 238.171.84.41
check 238.171.84.41
This is not a Class A, B, or C IP address.
ret 0
~/ipv4]$./test 192.168.2.3
check 192.168.2.3
This is a Class C IP address.
ret 1
```
至此,基本得到了比較完美的判斷,但有沒有漏洞呢?留給讀者自己去思考吧。
# 5 高階版本
有經(jīng)驗的程序一定會發(fā)現(xiàn),上面的各個判斷真的號麻煩啊!
每個case都需要這樣去比較判斷,那得多費勁啊!
有沒有更加清爽一點的高階方法啊?
答案當然是有的,這個時候你就需要了解一下:**正則表達式** 了。
很多主流的編程語言都有標準庫來支持正則表達式,那么C語言里面有沒有呢?
其實C語言里面也是可以用正則表達式的,這個先留個懸念,且聽下回分解。
歡迎打擊提前預習下:[正則表達式語言 - 快速參考 | Microsoft Learn](https://learn.microsoft.com/zh-cn/dotnet/standard/base-types/regular-expression-language-quick-reference)
# 6 完整測試用例
本小節(jié)給大家補充一下各種測試用例,希望對大家測試代碼有幫助:
```c
合法的測試輸入
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
```
測試用例是不斷豐富的,歡迎大家來補充。
審核編輯黃宇
-
C語言
+關注
關注
180文章
7604瀏覽量
136696 -
IPv4
+關注
關注
0文章
142瀏覽量
19890
發(fā)布評論請先 登錄
相關推薦
評論