多种IPV6类型解析:获取IPV6地址、IPV6地址范围、PV6地址/掩码等;获取 重复的、去重后的、错误及无效的、重复次数等

1、适用于多个 IPV6地址 格式,例如:2001:db8::1,2001:db8::10,2001:db8::1,2001:db8::10,2001:db8::1/127,2001:db8::1-2001:db8::10

// 解析:IPV6地址、IPV6地址范围、PV6地址/前级长度  获取 重复的、去重后的、错误及无效的、重复次数的获取等
export const ipv6IpAnalysis = (ipv6Ip) => {
  // 将字符串 拆分成数组
  const dataArray = ipv6Ip.split(',');
  // 判断IPv6地址是否有效
  const isValidIPv6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;

  const ipArray = []; // 正常 IPV6地址
  const ipScopeArray = []; // 正常 IPV6地址范围
  const ipsubnetArray = []; // 正常 IPV6地址/前级长度
  const mistakeArray = [];//错误IP地址
  const ipArrays = [];// 标准16进制 IPV6地址
  // 将数组 按照不同的类型 拆分到不同的数组中 
  const ipArraySplit = dataArray.filter(ip => !ip.includes('/') && !ip.includes('-'));  // IPV6地址
  const ipScopeArraySplit = dataArray.filter(ip => ip.includes('-'));  // IPV6地址范围
  const ipsubnetArraySplit = dataArray.filter(ip => ip.includes('/')); // IPV6地址/前级长度

  // 获取IPV6地址中 合规的 与 错误的
  ipArraySplit.forEach((item) => {
    if (isValidIPv6.test(item)) {
      // 获取合规的 IPV6地址中  并转转化 为 转标准8组,16进制的标准 IPV6地址
      ipArray.push(item);
      ipArrays.push(normalizeIPv6Address(item));
    } else {
      mistakeArray.push(item);
    }
  });

  // 获取IPV6地址范围中 合规的 与 错误的
  ipScopeArraySplit.forEach((item) => {
    const [start, end] = item.split("-");
    // 判断 开始、结束位置  地址是否有效
    if (isValidIPv6.test(start) && isValidIPv6.test(end)) {
      // 判断   前 < 后 
      //  这里normalizeIPv6Address(start), normalizeIPv6Address(end)  将 开始、结束位置转化为 ( 标准8组,16进制的标准的IPV6地址)
      if (isValidIPv6Range(normalizeIPv6Address(start), normalizeIPv6Address(end))) {
        // 符合要求
        ipScopeArray.push(item)
      } else {
        // 错误范围
        mistakeArray.push(item)
      }
    } else {
      // 开始、结束位置:存在地址 不有效不合规
      mistakeArray.push(item)
    }
  })

  // 将IPV6地址 转标准8组,16进制的标准 IPV6地址
  function normalizeIPv6Address (address) {
    // 以下正则匹配是否为IPv4转换为的IPv6地址
    const ipv4Mapped = address.match(/(.*)\b([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})$/i);
    if (ipv4Mapped) {
      // IPv4-mapped IPv6 地址的前缀是一系列的字符加冒号 (:)
      // 如果地址本身已经以 (:), 结尾, 这里就直接使用原字符串作为 IPv6 前缀
      // 如果没有结尾 (:), 则需要在原字符串后添加一个 (:)
      const ipv6Prefix = ipv4Mapped[1].endsWith(':') ? ipv4Mapped[1] : ipv4Mapped[1] + ':';

      // 将IPv4地址转换为16进位,并将其放入一个数组
      const ipv4Part = ipv4Mapped[2].split('.')
        .map(num => parseInt(num, 10).toString(16).padStart(2, '0'))
        .reduce((pair, num, idx) => (
          (idx % 2 === 0) ? [...pair, num] : [...pair.slice(0, -1), pair.slice(-1) + num]
        ), []);
      address = ipv6Prefix + ipv4Part.join(':');
    }
    // 将IPv6地址按冒号拆分成一个部件数组
    let parts = address.split(':');
    let output = [];
    // 遍历地址部位数组
    for (let i = 0; i < parts.length; i++) {
      if (parts[i] === '') {
        // 处理0值段, 用 4 个零 (0000) 占位
        let zerosNeeded = 8 - output.length - (parts.length - i - 1);
        for (let j = 0; j < zerosNeeded; j++) {
          output.push('0000');
        }
      } else {
        // 对于正常的部位,将其填充为 4 位 (0-9、A-F)
        let normalizedPart = parts[i].padStart(4, '0');
        output.push(normalizedPart);
      }
    }
    // 合并重新整理过的部位并添加冒号来生成标准IPv6地址
    return output.join(':');
  }
  // 判断范围是否有效
  function isValidIPv6Range (start, end) {
    // 解析起始地址和结束地址为数值数组
    const startParts = start.split(':').map(part => parseInt(part, 16));
    const endParts = end.split(':').map(part => parseInt(part, 16));
    // 比较地址的每个部分
    for (let i = 0; i < 8; i++) {
      if (startParts[i] > endParts[i]) {
        // 如果起始地址的某个部分大于结束地址的对应部分,则范围无效
        return false;
      } else if (startParts[i] < endParts[i]) {
        // 如果起始地址的某个部分小于结束地址的对应部分,则范围有效
        return true;
      }
      // 如果起始地址的某个部分等于结束地址的对应部分,则继续比较下一个部分
    }
    // 如果所有部分都相等,则范围无效
    return false;
  }

  // 获取IPV6地址 / 前级长度中 合规的 与 错误的
  ipsubnetArraySplit.forEach((item) => {
    if (isValidIPv6Address(item)) {
      ipsubnetArray.push(item);
    } else {
      mistakeArray.push(item);
    }
  });

  // 判断是否 有效合规
  function isValidIPv6Address (ip) {
    const halves = ip.split('/'); // 以斜线将IP地址和前缀长度分开
    // 判断 否是 存在一个斜杠
    if (halves.length !== 2) return false;
    //前缀长度为0 或 长度大于128或前缀长度不是数字等则返回false
    const prefix = Number(halves[1]);
    if (isNaN(prefix) || prefix < 0 || prefix > 128) return false;
    // 判断前部分地址 在 Ipv6中 合规
    if (!isValidIPv6.test(halves[0])) return false;// 斜杠前的地址 不合规,无效时 返回false
    const groups = normalizeIPv6Address(halves[0]).split(':'); //  将地址转化为标准的8组16进制地址,以冒号将IP地址的各个段分开
    let blanks = 0; // 记录空段数
    const validGroup = /^([0-9a-f]{1,4})$/i; // 限制 IPv6 的每个段只能由十六进制数字和字母组成,长度最多为四个字符
    // 对分开的每个段进行判断和验证是否符合 IPv6 地址标准
    for (let i = 0; i < groups.length; i++) {
      if (groups[i] === "") blanks++; // 如果段为空,则空段数加1
      else if (!validGroup.test(groups[i])) return false; // 如果段不符合规定,则返回 false
    }
    if (blanks > 1) return false; // 如果空段数多于 1,则返回 false
    return true;
  }

  // 将IPV6地址/前级长度 转化为 IPV6地址范围
  function ipv6Range (cidr) {
    // 将地址和前缀长度分离
    const [ip, mask] = cidr.split('/');
    // 将前缀长度转换为数字
    const maskBits = Number(mask);
    // 将 IPv6 地址扩展为完整的 8 个部分
    const ipParts = ip.split(':');
    const missingParts = 8 - ipParts.length;
    const expandedParts = [];
    ipParts.forEach((part) => {
      if (part === '') {
        for (let i = 0; i <= missingParts; i++) {
          expandedParts.push('0');
        }
      } else {
        expandedParts.push(part);
      }
    });
    // 计算 IPv6 地址范围
    const rangeStart = [];
    const rangeEnd = [];
    let remainingMaskBits = maskBits;
    expandedParts.forEach((part) => {
      const partInt = parseInt(part, 16);
      if (remainingMaskBits >= 16) {
        rangeStart.push(partInt);
        rangeEnd.push(partInt);
        remainingMaskBits -= 16;
      } else {
        const partMask = (1 << (16 - remainingMaskBits)) - 1;
        rangeStart.push(partInt & ~partMask);
        rangeEnd.push(partInt | partMask);
        remainingMaskBits = 0;
      }
    });
    // 将范围转换为字符串
    const rangeStartString = rangeStart.map((part) => part.toString(16).padStart(4, '0')).join(':');
    const rangeEndString = rangeEnd.map((part) => part.toString(16).padStart(4, '0')).join(':');
    return rangeStartString + '-' + rangeEndString;
  }

  // 将IPV6地址/前级长度(ipsubnetArray) 转化为 IPV6地址范围(ipScopeArray)
  const ipv6ConversionArray = []
  for (let i = 0; i < ipsubnetArray.length; i++) {
    ipv6ConversionArray.push(ipv6Range(ipsubnetArray[i]))
  }

  // IPV6地址范围 转化标准格式IPV6地址范围,再解析成 标准 单个 IPV6地址 
  function parseIPv6AddressRange (ipv6RangeParse) {
    // 将范围字符串分解成起始地址和结束地址
    const [startAddress, endAddress] = ipv6RangeParse.split('-').map(address => normalizeIPv6Address(address));
    // 将起始地址和结束地址从字符串转换为数字数组
    const startAddressParts = startAddress.split(':').map(part => parseInt(part, 16));
    const endAddressParts = endAddress.split(':').map(part => parseInt(part, 16));
    // 计算范围内的地址数量
    const numAddresses = endAddressParts.reduce((total, part, idx) => {
      const diff = part - startAddressParts[idx];
      return (total === null) ? diff : total * 65536 + diff;
    }, null);

    // 如果数量超过 1万,则提示数据量过大,返回
    if (numAddresses > 10000) return { num: numAddresses, mag: '数据量过大,请拆分' }
    // 生成所有地址并添加到数组中
    let output = [];
    for (let i = 0; i <= numAddresses; i++) {
      let addressParts = startAddressParts.slice();
      let remainder = i;
      for (let j = addressParts.length - 1; j >= 0; j--) {
        const diff = remainder % 65536;
        addressParts[j] += diff;
        remainder = Math.floor(remainder / 65536);
        if (remainder === 0) {
          break;
        }
      }
      output.push(addressParts.map(part => part.toString(16).padStart(4, '0')).join(':'));
    }
    return output;
  }

  // 将 IPV6地址范围 数据进行处理, 其中将 ipv6ConversionArray 与 ipScopeArray 数组进行合并成  ipv6AllScopeArray
  let ipv6AllScopeArray = [...ipv6ConversionArray, ...ipScopeArray] //all  所有 IPV6地址范围
  // 因为  IPV6地址范围 与 将IPV6地址/前级长度 拆分成单个 IPV6地址 是数据量可能很大,这里限制到了1万条 超过1万条时return  { num: numAddresses, mag: '数据量过大,请拆分' }
  let dataOverload = [] //数据量过大时使用
  let standardIpArray = []// 这里 储存 转换后 单个标准的IPV6地址
  // 处理数据过大
  for (let i = 0; i < ipv6AllScopeArray.length; i++) {
    if (parseIPv6AddressRange(ipv6AllScopeArray[i]).mag == undefined) {
      for (let j = 0; j < parseIPv6AddressRange(ipv6AllScopeArray[i]).length; j++) {
        standardIpArray.push(parseIPv6AddressRange(ipv6AllScopeArray[i])[j])
      }
    } else {
      dataOverload = parseIPv6AddressRange(ipv6AllScopeArray[i])
      break
    }
  }

  // 将 ipArrays(已转化) 与  standardIpArray  数据合并  用于获取 重复的、去重的后数据
  const ipv6AllStandardArray = [...ipArrays, ...standardIpArray] //所有 标准 IPV6地址 数组

  let nonredundant = [];// 去重后的地址
  let duplicateData = [];// 重复的地址
  let hintArray = []; // 重复的地址( XXXXXX 出现 N 次 )

  // 去重
  nonredundant = [...new Set(ipv6AllStandardArray)]
  // 获取重复元素
  duplicateData = ipv6AllStandardArray.filter((item, index) => ipv6AllStandardArray.indexOf(item) !== index);


  // 获取重复的ip 用于提示
  const duplicates = ipv6AllStandardArray.reduce((acc, curr) => {
    acc[curr] ? acc[curr]++ : acc[curr] = 1;
    return acc;
  }, {});
  // 获取重复的ip  格式为用于提示
  hintArray = Object.entries(duplicates)
    .filter(([key, value]) => value > 1)
    .map(([key, value]) => `${key}, 出现: ${value} 次`);




  // console.log(ipArray, '单个');
  // console.log(ipArrays, '单个标准');
  // console.log(ipScopeArray, '范围');
  // console.log(ipsubnetArray, '地址/前级');
  // console.log(mistakeArray, '错误的');
  // console.log(ipv6ConversionArray, '地址/前级转范围');
  // console.log(ipv6AllScopeArray, '所有范围');
  // console.log(dataOverload, '数据量过大使用');
  // console.log(nonredundant, '去重后的地址');
  // console.log(duplicateData, '重复的地址');
  // console.log(hintArray, '提示使用');

  return { ipArray, ipArrays, ipScopeArray, ipsubnetArray, mistakeArray, ipv6ConversionArray, ipv6AllScopeArray, dataOverload, nonredundant, duplicateData, hintArray }
};

2、验证:全新vue2项目(如有错误,请踊跃私信提出)

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值