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项目(如有错误,请踊跃私信提出)
版权声明:本文为Smile_Gently原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。