vue开发中,会使用到自定义公式校验合法性,判断公式是否符合逻辑, 整理个人使用过的自定义公式页面保存时对输入的字符串进行校验的一套规则
(文章最后有完整代码)
目录
1. 正则判断
const re = /[^0-9\(\)\+\-\×\÷\.\#\{\}]{1,}/ // 判断输入字符合法性的正则(只能输入数字,+-×÷,(),#{})
const re = /#\{(.+?)\}/g // 取出#{}中的字符正则
const re = /\((.+?)\)/g // 匹配出所有括号,返回数组
if (/\(\)/.test(str)) throw new Error('括号中至少维护一个运算公式')
// 错误情况,( 后面是运算符
if (/\([\+\-\×\÷]/.test(str)) { throw new Error('运算公式配置不正确') }
// 错误情况,) 前面是运算符
if (/[\+\-\×\÷]\)/.test(str)) { throw new Error('运算公式配置不正确') }
// 错误情况,( 前面不是运算符 或 空
if (/[^\+\-\×\÷\(\s]\(/.test(str)) { throw new Error('运算公式配置不正确') }
// 错误情况,) 后面不是运算符 或 空
if (/\)[^\+\-\×\÷\)\s]/.test(str)) { throw new Error('运算公式配置不正确') }
// 错误情况,运算符号不能在首末位
if (/^[\+\-\×\÷.]|[\+\-\×\÷.]$/.test(str)) { throw new Error('运算公式配置不正确') }
// 错误情况,运算符连续
if (/[\+\-\*\/]{2,}/.test(str)) { throw new Error('运算公式配置不正确') }
// 错误情况,#{}后面不是 运算符或 ) 或 ''
if (/#\{.+\}[^\+\-\×\÷\)\s]/.test(str)) throw new Error('运算公式配置不正确')
// 错误情况,#{}前面不是 运算符或 ( 或 ''
if (/[^\+\-\×\÷\(\s]#\{.+\}/.test(str)) throw new Error('运算公式配置不正确')
2. 校验数字(输入数字不超过十位数,不超过两位小数)
// 错误情况,.后面不是数字
if (/\.[^0-9]/.test(str)) throw new Error('公式不合法')
// 错误情况,.前面不是数字
if (/[^0-9]\./.test(str)) throw new Error('公式不合法')
// 判断数字是否超过10位,小数点是否超过两位
const arrNum = []
let tmp = ''
for (var i = 0; i < str.length; i++) {
if (
(formula.charAt(i) <= '9' && formula.charAt(i) >= '0') ||
formula.charAt(i) == '.'
) {
tmp += formula.charAt(i)
} else {
if (tmp) {
arrNum.push(tmp)
tmp = ''
}
}
} // 在循环外加一个判断,是因为当字符串遍历到最后一位9的时候,不会走else里面的内容,无法加到数组
if (tmp) {
arrNum.push(tmp)
tmp = ''
}
arrNum.forEach((item) => {
if (parseInt(item) > 999999999) throw new Error('最大不超过十位数')
if (item.split('.')[1] && item.split('.')[1].length > 2) { throw new Error('最多输入两位小数') }
})
3. 校验括号(嵌套括号是否符合要求)
const left = [] // 遇到左括号存入数量和索引
const right = [] // 遇到右括号存入数量和索引
let lnum = 1 // 记录左括号的数量
let rnum = 1 // 记录右括号的数量
const str2 = str.split('') // 将字符串转为数组
for (let iii = 0; iii < str2.length; iii++) {
if (str2[iii] == '(') { // 循环遇到左括号
left.push({ num: lnum++, index: iii }) // 存入数量和索引
} else if (str2[iii] == ')') { // 循环遇到右括号
right.push({ num: rnum++, index: iii }) // 存入数量和索引
const xyz = str2.slice(left[left.length - 1].index, right[right.length - 1].index + 1) // 取出遇到右括号时与上一个左括号之间的内容
// 校验xyz 判断括号中是否包含+- × ÷
const b = xyz.join('').match(regex1) // 将数组转回字符串进行取括号中的内容
if (b) {
// 判断字符串中是否有括号
for (let i = 0; i < b.length; i++) {
if (b[i].match(/×|-|[÷]|[+]/)) {
// 匹配加减乘除
// console.log('有加减乘除')
} else {
// console.log('没有加减乘除')
throw new Error('括号中至少维护一个运算公式')
}
}
} else {
console.log('没有括号')
}
// 删除匹配完后两个位置的括号,替换成空格
str2.splice(left[left.length - 1].index, 1, ' ')
str2.splice(right[right.length - 1].index, 1, ' ')
// 删除数组中最后一项,继续匹配
left.pop()
right.pop()
}
}
4. (完整代码文件)
const re = /[^0-9\(\)\+\-\×\÷\.\#\{\}]{1,}/ // 判断输入字符合法性的正则(只能输入数字,+-×÷,(),#{})
const ru = /#\{(.+?)\}/g // 取出#{}中的字符正则
const regex1 = /\((.+?)\)/g // 匹配出所有括号,返回数组
// formula为传入需要校验的字符串; ruleArr为定义的系统提供的字段数组(#{}包裹的内容)
export const validatedFunc = function(formula, ruleArr) {
// 剔除空白符
formula = formula.replace(/\s/g, '')
// 如果输入了不合法的字符,直接返回不合法(除去#{}中的内容)
const str = formula.replace(/#\{(.+?)\}/g, '')
let result = false
result = re.test(str)
if (result) throw new Error('请选择系统提供的字段和运算符')
// 空括号
if (/\(\)/.test(formula)) throw new Error('括号中至少维护一个运算公式')
const left = [] // 遇到左括号存入数量和索引
const right = [] // 遇到右括号存入数量和索引
let lnum = 1 // 记录左括号的数量
let rnum = 1 // 记录右括号的数量
const str2 = formula.split('') // 将字符串转为数组
for (let iii = 0; iii < str2.length; iii++) {
if (str2[iii] == '(') { // 循环遇到左括号
left.push({ num: lnum++, index: iii }) // 存入数量和索引
} else if (str2[iii] == ')') { // 循环遇到右括号
right.push({ num: rnum++, index: iii }) // 存入数量和索引
const xyz = str2.slice(left[left.length - 1].index, right[right.length - 1].index + 1) // 取出遇到右括号时与上一个左括号之间的内容
// 校验xyz
// 判断括号中是否包含+- × ÷
const b = xyz.join('').match(regex1) // 将数组转回字符串进行取括号中的内容
if (b) {
// 判断字符串中是否有括号
for (let i = 0; i < b.length; i++) {
if (b[i].match(/×|-|[÷]|[+]/)) {
// 匹配加减乘除
// console.log('有加减乘除')
} else {
// console.log('没有加减乘除')
throw new Error('括号中至少维护一个运算公式')
}
}
} else {
console.log('没有括号')
}
// 删除匹配完后两个位置的括号,替换成空格
str2.splice(left[left.length - 1].index, 1, ' ')
str2.splice(right[right.length - 1].index, 1, ' ')
// 删除数组中最后一项,继续匹配
left.pop()
right.pop()
}
}
// 判断数字相关
// 错误情况,.后面不是数字
if (/\.[^0-9]/.test(formula)) throw new Error('公式不合法')
// 错误情况,.前面不是数字
if (/[^0-9]\./.test(formula)) throw new Error('公式不合法')
// 判断数字是否超过10位,小数点是否超过两位
const arrNum = []
let tmp = ''
for (var i = 0; i < formula.length; i++) {
if (
(formula.charAt(i) <= '9' && formula.charAt(i) >= '0') ||
formula.charAt(i) == '.'
) {
tmp += formula.charAt(i)
} else {
if (tmp) {
arrNum.push(tmp)
tmp = ''
}
}
} // 在循环外加一个判断,是因为当字符串遍历到最后一位9的时候,不会走else里面的内容,无法加到数组
if (tmp) {
arrNum.push(tmp)
tmp = ''
}
arrNum.forEach((item) => {
if (parseInt(item) > 999999999) throw new Error('最大不超过十位数')
if (item.split('.')[1] && item.split('.')[1].length > 2) { throw new Error('最多输入两位小数') }
})
// 判断括号相关
// 括号不配对
const stack = []
for (var q = 0, item; q < formula.length; q++) {
item = formula.charAt(q)
if (item === '(') {
stack.push('(')
} else if (item === ')') {
if (stack.length > 0) {
stack.pop()
} else {
return false
}
}
}
if (stack.length !== 0) throw new Error('括号必须成对出现')
// 错误情况,( 后面是运算符
if (/\([\+\-\×\÷]/.test(formula)) { throw new Error('运算公式配置不正确') }
// 错误情况,) 前面是运算符
if (/[\+\-\×\÷]\)/.test(formula)) { throw new Error('运算公式配置不正确') }
// 错误情况,( 前面不是运算符 或 空
if (/[^\+\-\×\÷\(\s]\(/.test(formula)) { throw new Error('运算公式配置不正确') }
// 错误情况,) 后面不是运算符 或 空
if (/\)[^\+\-\×\÷\)\s]/.test(formula)) { throw new Error('运算公式配置不正确') }
// 错误情况,运算符号不能在首末位
if (/^[\+\-\×\÷.]|[\+\-\×\÷.]$/.test(formula)) { throw new Error('运算公式配置不正确') }
// 错误情况,运算符连续
if (/[\+\-\*\/]{2,}/.test(formula)) { throw new Error('运算公式配置不正确') }
// 错误情况,#{}后面不是 运算符或 ) 或 ''
// console.log(/#\{.+\}[^\+\-\×\÷]/.test(formula), '.......................')
if (/#\{.+\}[^\+\-\×\÷\)\s]/.test(formula)) throw new Error('运算公式配置不正确')
// 错误情况,#{}前面不是 运算符或 ( 或 ''
if (/[^\+\-\×\÷\(\s]#\{.+\}/.test(formula)) throw new Error('运算公式配置不正确')
// 判断不是系统提供的字段(#{}包含的内容)
const e = formula.match(ru)
if (e) {
e.forEach(item => {
const o = item.match(/#\{(.+)\}/)[1]
// console.log(item.match(/#\{(.+)\}/)[1])
const dd = ruleArr.filter(val => o == val.ruleName)
// console.log(dd, 'dddddddddddddddddddd')
if (!dd.length) throw new Error('请选择系统提供的字段')
})
}
return true
}