【较难】算法nodeJs:识别有效的IP地址和掩码并进行分类统计

91 阅读4分钟

描述

在本题中,我们需要处理地址信息,其由 IP 地址和子网掩码组成,这两者均形如 " . . . " ,由四段数字组成,每段数字之间以点分隔。

我们定义五类 IP 地址:
∙A类:"1.0.0.0"-"126.255.255.255" ;
∙B类:"128.0.0.0"-"191.255.255.255" ;
∙C类:"192.0.0.0"-"223.255.255.255" ;
∙D类:"224.0.0.0"-"239.255.255.255" ;
∙E类:"240.0.0.0"-"255.255.255.255" 。

我们定义私有 IP 地址:
∙"10.0.0.0"-"10.255.255.255" ;
∙"172.16.0.0"-"172.31.255.255" ;
∙"192.168.0.0"-"192.168.255.255" 。

我们定义合法的子网掩码:
∙将 IP 地址转换为二进制后,必须由若干个连续的 1 后跟若干个连续的 0 组成;
∙例如,"1.1.1.5" 是一个非法的子网掩码,因为它转换为二进制后为 1.1.1.101 ,中间出现了 0 后又出现了 1 ;
∙注意,全为 1 或全为 0 的子网掩码也是非法的。

现在,你需要分类统计ABCDE类地址的数量、错误 IP 或错误子网掩码的数量、私有 IP 的数量。

特别地,我们还有以下提示:
∙类似于 "0. . ." 和 "127. . ." 的 IP 地址不属于上述输入的任意一类,也不属于不合法 IP 地址;
∙一个 IP 地址既可以是私有 IP 地址,也可以是五类 IP 地址之一,计数时请分别计入。

输入描述:

本题将会给出 1≦T≦1000 条地址信息,确切数字未知,您需要一直读入直到文件结尾;您也可以参考 牛客网在线判题系统使用帮助 获得更多的使用帮助。每条地址信息描述如下:

在一行上先输入一个字符串,代表 IP 地址;随后,在同一行输入一个字符串,代表子网掩码;使用 " " 分隔。

输出描述:

在一行上输出七个整数,分别代表ABCDE类地址的数量、错误 IP 或错误子网掩码的数量、私有 IP 的数量。

const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;

void (async function () {
    // Write your code here
    /**
     * 有效掩码
     * 将十进制数255.255.248.0转换为二进制中间不能有0
     * 如 11111111.11111111.11111000.00000000
     */
    // ip 数组 和 掩码数组
    const list = [];
    //输出内容
    let a = [],
        b = [],
        c = [],
        d = [],
        e = [],
        err = [],
        private = [];
    //获取所有ip和掩码
    while ((line = await readline())) {
        let tokens = line.split("~");
        list.push({
            ip: tokens[0],
            maskCode: tokens[1],
        });
    }

    /**
     * 判断ip是否合法
     * 1、除了.和数字以外没有其他字符
     * 2、.的个数为3
     * 3、每个数字在 0-255 之间,如果数字大于0,则不能以0开头
     */
    const ipIsPass = (ip = "") => {
        //除了数字和.还有其他字符,判定为不合法
        if (/[^0-9.]/.test(ip)) {
            return false;
        }
        const arr = ip.split(".");
        //必须是四位
        if (arr.length != 4) {
            return false;
        }
        //每一位必须是数字;并且为 0-255;如果大于0,不能以0开头
        for (let i = 0; i < arr.length; i++) {
            const item = arr[i];
            //不能为空
            if (item.length === 0) {
                return false;
            }
            //不在0-255之间
            if (Number(item) < 0 || Number(item) > 255) {
                return false;
            }
            //如果大于 0,不能以 0 开头
            if (Number(item) > 0 && item.startsWith(0)) {
                return false;
            }
        }
        return true;
    };
    //判断掩码是否合法
    //将子网掩码从点分十进制格式转换为32位的二进制数
    //检查二进制数中是否满足网络部分连续的位全为1,主机部分连续的位全为0
    const maskCodeIsPass = (maskCode = "") => {
        //传入的转换前的掩码,也需要满足ip的验证规则
        if (!ipIsPass(maskCode)) {
            return false;
        }
        //拆分出每一位的数字
        const arr = maskCode.split(".");
        //32位字符串存储
        let res = "";
        //将拆分出的四个数字转换成8位的二进制字符串,然后拼接成一个32位的字符串
        arr.map((item) => {
            //拼接,不足8位补0
            res += Number(item).toString(2).padStart(8, "0");
        });
        //1开始并连续,0结尾并连续
        return /^1+0+$/.test(res);
    };

    for (let i = 0; i < list.length; i++) {
        const item = list[i];
        const ip = item.ip;
        const maskCode = item.maskCode;
        //获取ip的第一个数字和第二个数字
        const first = ip.split(".")[0];
        //【0.*.*.*】和【127.*.*.*】的IP地址 不计数
        if (first == 0 || first == 127) {
            continue;
        }
        //ip 和 maskCode 都合法,则进行分类计数。否则计入错误列表
        if (ipIsPass(ip) && maskCodeIsPass(maskCode)) {
            //ip 的第二个数字
            const second = ip.split(".")[1];
            //私网ip
            if (
                first == 10 ||
                (first == 172 && second >= 16 && second <= 31) ||
                (first == 192 && second == 168)
            ) {
                private.push(ip);
            }
            //A,B,C,D,E五类
            if (first >= 1 && first <= 126) {
                a.push(ip);
            } else if (first >= 128 && first <= 191) {
                b.push(ip);
            } else if (first >= 192 && first <= 223) {
                c.push(ip);
            } else if (first >= 224 && first <= 239) {
                d.push(ip);
            } else if (first >= 240 && first <= 255) {
                e.push(ip);
            }
        } else {
            //ip 或者 掩码不合法
            err.push(item);
        }
    }

    console.log(
        [
            a.length,
            b.length,
            c.length,
            d.length,
            e.length,
            err.length,
            private.length,
        ].join(" ")
    );
})();