开始讲解之前,我先说一下我的感受,虽然这道题只是一道模拟题目,并没有考察算法,但是作者写出来的时候是十分开心的,因为这道题目利用是我第一道基本直接利用STL内的函数写出来的题目,所以作者觉得意义重大,所以就拿出来分享给大家。
题目如下:
右鸽最近在学计算机⽹络原理,所以想着把学到的知识跟你分享⼀下。
IPv6(Internet Protocol Version 6) 即互联⽹协议第六版, IPv6 的出现就是为了替代 IPv4 ,主要原因是 IPv4 的地址不够⽤。随着科技的发展,我们的⽣活⽔平不断提⾼,很多空调,⾳响,电视机都要上⽹,导致⽹络上IP地址不够⽤,所以升级成 IPv6 ,有点夸张的是, IPv6 就可以满⾜让每⼀个沙⼦都有编号地址可以上⽹。
下⾯简要说明⼀下 IPv4 地址和 IPv6 地址的特点。
IPv4 地址由⼗进制数和点("..")来表⽰,每个地址包含4个⼗进制数,其范围为 0 - 2550−255, ⽤("..")分割。⽐如172.16.254.1172.16.254.1 。
同时,IPv4 地址内的数不会有前导 00 。⽐如,地址 172.16.254.01172.16.254.01 是不合法的。
IPv6 地址由 88 组 1616 进制的数字来表⽰,每组表⽰ 1616 ⽐特。这些组数字通过 ("::")分割。
⽐如, 2001:0db8:85a3:0000:0000:8a2e:0370:73342001:0db8:85a3:0000:0000:8a2e:0370:7334 是⼀个有效的地址。
⽽且,我们可以加⼊⼀些以 00 开头的数字(即允许有前导00的情况),字母可以使⽤⼤写,也可以是⼩写。
所以, 2001:db8:85a3:0:0:8A2E:0370:73342001:db8:85a3:0:0:8A2E:0370:7334 也是⼀个有效的 IPv6 地址 (即忽略 00 开头,忽略⼤⼩写)。
然⽽,我们不能因为某个组的值为 00 ,⽽使⽤⼀个空的组,以⾄于出现 (::::) 的情况。
⽐如,2001:0db8:85a3::8A2E:0370:73342001:0db8:85a3::8A2E:0370:7334 是⽆效的 IPv6 地址。
同时,在 IPv6 地址中,每组中的数字超过4位也是不被允许的,因为这样他就超过 1616 ⽐特了(即使超过的位是 00 也不被允许)。
⽐如, 02001:0db8:85a3:0000:0000:8a2e:0370:733402001:0db8:85a3:0000:0000:8a2e:0370:7334 是⽆效的,因为 0200102001 超过了 44 位。
右鸽不想让你养成⽩嫖的不良习惯,于是想让聪明的你即学即⽤,帮右鸽编写程序来验证输⼊的字符串是否是有效的 IPv4 或 IPv6 地址。
输入:
输⼊⼀个整数 n(1≤n≤1000) 接下来的 n ⾏,每⾏输⼊⼀个需要判断的字符串
输出:
对于每⼀个字符串输出⼀⾏。
如果输⼊的字符串是有效的 IPv4 ,则输出 IPv4 ;
如果输⼊的字符串是有效的 IPv6 ,则输出 IPv6 ;
如果输⼊的字符串不合法,则输出 Invalid 。
样例输入:
5 172.16.0.1 172.16.0.01 2001:0db8:85a3:0:0000:8A2E:0370:7334 2001:0db8:85a3:0:0000:8A2E:0370:733w 02001:0db8:85a3:0:0000:8a2e:0370:7334
样例输出:
IPv4 Invalid IPv6 Invalid Invalid
这道题的一个难点就是需要判断的条件太多,并且需要对字符串和整形数据进行转换,这里就可以用到一个函数stoi(),这个函数很好记忆,s即string,to就是到达的意思,i就是int整形数据的意思,里面的参数就是一个字符串,在解决好了转换问题,那么我们怎么将每一段的数据抽取出来呢?我们注意到每个地址都有“.” 或者“:”符号,因此我们可以利用string类的成员函数find()找到这个标志的位置,再利用string类的成员函数substr()获取字串,在对这个字串加以判断,最后达到我们想要的结果。
tips:这里有个小细节是需要自己发现的,就是判断ipv6的时候还要对里面的字母加以判断,因为是十六进制,所以字母不会超过F或者f,作者刚开始就漏掉了这一点,不过后面想到了。
这道题目如果不灵活运用这些函数的话,那么代码量将会很大,大概是用函数方法的两倍代码量
AC代码:
#include <iostream>
#include <string>
using namespace std;
string b[1001]; //该数组用于储存结果
bool judge(string str)//判断ipv6是否有字符超过十六进制的使用范围
{
for (int i = 0; i < str.size(); i++)
{
if (str[i] > 70 && str[i] <= 90 || str[i] > 111 && str[i] <= 122)
return true;
}
return false;
}
int main()
{
int n, j = 0;
string a;
cin >> n;
while (n--) //n次访问
{
cin >> a;
int pos = a.find('.'); //每次判断前先判断字符串类型
int pos1 = a.find(':');
if (pos != -1) //如果是ipv4
{
while (pos != -1)
{
pos = a.find('.'); //更新'.'的位置
string str = a.substr(0, pos);
if (a[0] == '0' && str.size() > 1 || stoi(str) > 255 )
{
b[j] = "Invalid";
j++;
pos = 0; //将pos置为0防止多次判断
break;
}
else
{
a = a.erase(0, pos + 1); //更新字符串
}
}
if (pos == -1) // 如果将前三组数据判断完后,那么判断最后一组数据
{
if (a[0] == '0' && a.size() > 1 || stoi(a) > 255)
{
b[j] = "Invalid";
j++;
}
else
{
b[j] = "IPv4";
j++;
}
}
}
else if (pos1 != -1)// 如果改字符串是ipv6,其实到这里和判断ipv4差不多,只是条件不太一样
{
while (pos1 != -1)
{
pos1 = a.find(':'); //更新':'的位置
string str = a.substr(0, pos1);
if (str.size() > 4 || a[pos1 + 1] == ':' || judge(str))
{
b[j] = "Invalid";
j++;
pos1 = 0;
break;
}
else
{
a = a.erase(0, pos1 + 1);
}
}
if (pos1 == -1) //判断最后一组数据
{
if (a.size() > 4 || judge(a))
{
b[j] = "Invalid";
j++;
}
else
{
b[j] = "IPv6";
j++;
}
}
}
}
for (int i = 0; i < j; i++)
{
cout << b[i] << endl; //将结果遍历输出
}
return 0;
}