题目:如何判断一个IP是否是合法的IP,如输入:192.168.1.0,输出:合法;输入192.168.1.1222,输出:非法。
解答:先了解IP的格式,它的形式应该为:(1~255).(0~255).(0~255).(0~255)。那么可以有两种方法实现,一种是基于对字符串的处理,另一种是通过强大的正则表达式来判断。下面我将采用熟悉的Java来实现。
方法一:对字符串进行截取、分析等,代码如下:
public static void main(String[] args){
System.out.println("请输入要验证的IP地址:");
Scanner scanner = new Scanner(System.in);
String ipStr = scanner.next();
boolean isIpLegal = isIpLegal(ipStr);
if(isIpLegal) {
System.out.println("合法");
}
else{
System.out.println("非法");
}
}
public static boolean isIpLegal(String str){
//1.检查ip是否为空
if(str == null){
return false;
}
//2.检查ip长度,最短为:x.x.x.x(7位),最长为:xxx.xxx.xxx.xxx(15位)
if(str.length() < 7 || str.length() > 15){
return false;
}
//3.按"."分割字符串,并判断分割出来的个数,如果不是4个,则是非法IP
String[] arr = str.split("\\.");
if(arr.length != 4){
return false;
}
//4.对分割得到的每个字符串进行单独判断
for(int i = 0; i < arr.length; i++){
//对分割得到的每个字符串的每个字符进行逐一判断,如果不是数字0-9,则判定为非法IP
for(int j = 0; j < arr[i].length(); j++){
if (arr[i].charAt(j) < '0' || arr[i].charAt(j) > '9'){
return false;
}
}
}
//5.对拆分的每一个字符串进行转换成数字,并判断是否在0~255
for(int i = 0; i < arr.length; i++){
int temp = Integer.parseInt(arr[i]);
if(i == 0){
if (temp < 1 || temp > 255){
return false;
}
}
else{
if(temp < 0 || temp > 255){
return false;
}
}
}
//6.最后,如果经过前面验证都没返回到false,返回true
return true;
}
乍看这样的判断貌似没什么问题,但是在经过多次测试后发现,会存在三个问题。
(1)如果输入以小数点开头的非法IP,如:.x.x.x.或者.x.x.xx,编译运行后,在int temp = Integer.parseInt(arr[i])一行会有一个NumberFormatExceptin报错;
(2)如果输入以小数点结尾的非法IP,如:x.x.x.x.,最终得到的结果为“合法”;
(3)如果输入带0开头的IP,如:0x.0xx.0x.00x,最终得到的结果为“合法”;
问题(1):仔细分析是由于Java的split()方法截取时出的问题。如果输入为:.x.x.x.或者.x.x.xx的时候,split后得到的数组为arr[4]={“”,”x”, “x”, “x”}和arr[4]={“”,”x”, “x”, “xx”},就会使得后面对字符串转型时的int temp = Integer.parseInt(arr[i])那行报错NumberFormatException;
问题(2):如果输入为:x.x.x.x.,split后得到的数组为arr[4]={“x”,”x”, “x”, “x”},而不会是arr[4]={“x”,”x”, “x”, “x”, “”},根据后面的判断方法,会将此非法IP判定为合法IP。
问题(3):这是因为0xx,在Integer.parseInt()转型时会忽略掉这个0的,因此会通过判定,最后得到认为它是合法的IP。
解决方法:问题(1)(2)需要在截取字符串之前,对输入的字符串的首末字符进行判断,如果是小数点”.”,则判定为非法IP。问题(3)可以在第4步中加入判断,如果分割得到的每个字符串不是一位字符且以”0”开头,则判断为非法IP。完整代码如下:
public static void main(String[] args){
System.out.println("请输入要验证的IP地址:");
Scanner scanner = new Scanner(System.in);
String ipStr = scanner.next();
boolean isIpLegal = isIpLegal(ipStr);
if(isIpLegal) {
System.out.println(ipStr + " 合法");
}
else{
System.out.println(ipStr + " 非法!!!");
}
}
public static boolean isIpLegal(String str){
//1.检查ip是否为空
if(str == null){
return false;
}
//2.检查ip长度,最短为:x.x.x.x(7位),最长为:xxx.xxx.xxx.xxx(15位)
if(str.length() < 7 || str.length() > 15){
return false;
}
//3.解决问题(1)(2): 对输入字符串的首末字符判断,如果是"."则是非法IP
if(str.charAt(0) == '.' || str.charAt(str.length()-1) == '.'){
return false;
}
//4.按"."分割字符串,并判断分割出来的个数,如果不是4个,则是非法IP
String[] arr = str.split("\\.");
if(arr.length != 4){
return false;
}
//5.对分割出来的每个字符串进行单独判断
for(int i = 0; i < arr.length; i++){
//解决问题(3): 如果每个字符串不是一位字符,且以'0'开头,则是非法的IP,如:01.002.03.004
if(arr[i].length() > 1 && arr[i].charAt(0) == '0'){
return false;
}
//对每个字符串的每个字符进行逐一判断,如果不是数字0-9,则是非法的IP
for(int j = 0; j < arr[i].length(); j++){
if (arr[i].charAt(j) < '0' || arr[i].charAt(j) > '9'){
return false;
}
}
}
//6.对拆分的每一个字符串进行转换成数字,并判断是否在0~255
for(int i = 0; i < arr.length; i++){
int temp = Integer.parseInt(arr[i]);
if(i == 0){
if (temp < 1 || temp > 255){
return false;
}
}
else{
if(temp < 0 || temp > 255){
return false;
}
}
}
//7.最后,如果经过前面验证都没返回到false,返回true
return true;
}
方法二:采用正则表达式,代码如下:
public static void main(String[] args) {
System.out.println("请输入要验证的IP地址:");
Scanner scanner = new Scanner(System.in);
String ipStr = scanner.next();
isIpLegal(ipStr);
}
public static boolean isIpLegal(String ipStr) {
//ip地址范围:(1~255).(0~255).(0~255).(0~255)
String ipRegEx = "^([1-9]|([1-9][0-9])|(1[0-9][0-9])|(2[0-4][0-9])|(25[0-5]))(\\.([0-9]|([1-9][0-9])|(1[0-9][0-9])|(2[0-4][0-9])|(25[0-5]))){3}$";
//String ipRegEx = "^([1-9]|([1-9]\\d)|(1\\d{2})|(2[0-4]\\d)|(25[0-5]))(\\.(\\d|([1-9]\\d)|(1\\d{2})|(2[0-4]\\d)|(25[0-5]))){3}$";
//String ipRegEx = "^(([1-9]\\d?)|(1\\d{2})|(2[0-4]\\d)|(25[0-5]))(\\.(0|([1-9]\\d?)|(1\\d{2})|(2[0-4]\\d)|(25[0-5]))){3}$";
Pattern pattern = Pattern.compile(ipRegEx);
Matcher matcher = pattern.matcher(ipStr);
if (matcher.matches()) {
return true;
} else {
return false;
}
}
从上面两种方法来看,不得不感叹正则表达式的强大。通过正则直接匹配来判断,代码简洁许多。
为了方便测试,我们在main函数中写入一个数组,而不再进行一个个输入,测试代码如下:
方法1测试代码:
public static void main(String[] args){
String[] ipArr = new String[]{"abc", "123456789012345678", ".1.2.3", "1.2.3.4.", "1.2.3", "1.2.3.4.5", "1.02.003.014", "1a.2.3.4", "1.2a.3.4", "1.2.3a.4", "1.2.3.4a",
"0.1.2.3", "-1.1.2.3", "1.-1.2.3", "1.2.-1.3", "1.2.3.-1", "256.1.2.3", "1.256.2.3", "1.2.256.3", "1.2.3.256", "1234.123.123.123", "123.1234.123.123",
"123.123.1234.123", "123.123.123.1234", "123.123.123.123", "0.0.0.0", "1.0.0.0", "255.255.255.255", "11.22.33.44", ""};
for (int i = 0; i < ipArr.length; i++) {
boolean isIpLegal = isIpLegal(ipArr[i]);
if(isIpLegal) {
System.out.println(ipArr[i] + " 合法");
} else {
System.out.println(ipArr[i] + " 非法!!!");
}
}
}
方法2测试代码:
public static void main(String[] args) {
String[] ipArr = new String[]{"abc", "123456789012345678", ".1.2.3", "1.2.3.4.", "1.2.3", "1.2.3.4.5", "1.02.003.014", "1a.2.3.4", "1.2a.3.4", "1.2.3a.4", "1.2.3.4a",
"0.1.2.3", "-1.1.2.3", "1.-1.2.3", "1.2.-1.3", "1.2.3.-1", "256.1.2.3", "1.256.2.3", "1.2.256.3", "1.2.3.256", "1234.123.123.123", "123.1234.123.123",
"123.123.1234.123", "123.123.123.1234", "123.123.123.123", "0.0.0.0", "1.0.0.0", "255.255.255.255", "11.22.33.44", ""};
for (int i = 0; i < ipArr.length; i++) {
boolean isIpLegal = isIpLegal(ipArr[i]);
if (isIpLegal) {
System.out.println(ipArr[i] + " 合法");
} else {
System.out.println(ipArr[i] + " 非法!!!");
}
}
}
测试结果:
abc 非法!!!
123456789012345678 非法!!!
.1.2.3 非法!!!
1.2.3.4. 非法!!!
1.2.3 非法!!!
1.2.3.4.5 非法!!!
1.02.003.014 非法!!!
1a.2.3.4 非法!!!
1.2a.3.4 非法!!!
1.2.3a.4 非法!!!
1.2.3.4a 非法!!!
0.1.2.3 非法!!!
-1.1.2.3 非法!!!
1.-1.2.3 非法!!!
1.2.-1.3 非法!!!
1.2.3.-1 非法!!!
256.1.2.3 非法!!!
1.256.2.3 非法!!!
1.2.256.3 非法!!!
1.2.3.256 非法!!!
1234.123.123.123 非法!!!
123.1234.123.123 非法!!!
123.123.1234.123 非法!!!
123.123.123.1234 非法!!!
123.123.123.123 合法
0.0.0.0 非法!!!
1.0.0.0 合法
255.255.255.255 合法
11.22.33.44 合法
非法!!!