一、主要的类型与对象之间的关系:
二、自定义的公式函数(应用)
/*提取文本中连续匹配指定的字符集 N-M 次之间的第 x 个匹配到的连续字符串from : 文本或单元格,非文本会被自动转换成文本token : 提取第几个这种连续字符串,必须是正整数charSetRules : [NamedCharSet_Name1[, reverse1],NamedCharSet_Name2[,reverse2], ....]NamedCharSet_Name1/2/.../N : 第 N 个命名字符集的名称reverse1/2/.../N : 与上面字符集名称一一对应的字符集是否反转的描述,它们为true 时,它对应的字符集将被反转后,再进行匹配;如果省略,一律设为 false,即不反转字符集return : String,提取到的文本*/function GetMatchByNamedCharSet(from, token,minTimes, maxTimes, ...charSetRules) {{//0.检验并处理参数//0.1.检验并处理 from 参数if (typeof from != 'string') {if (Object.prototype.toString.call(from).slice(8, -1) === 'Range')from = from.Value().toString();elsefrom = from.toString();}if (from.length === 0) return '';//0.2.检验并处理 token 参数if (token === undefined) token = 1;if (typeof token != 'number' || Math.trunc(token) <= 0)throw new Error('参数 token 必须是正整数');elsetoken = Math.trunc(token);//0.3.检验并处理 charSetRules 参数//0.3.1.至少应该含有一个元素let isArgCharSetRulesOkey = charSetRules.length > 0;//0.3.2.只允许包含 String/Boolean 元素isArgCharSetRulesOkey = isArgCharSetRulesOkey &&charSetRules.every(x =>typeof x === 'boolean' ||typeof x === 'string');//0.3.3.第一个元素不能是 Boolean 值isArgCharSetRulesOkey = isArgCharSetRulesOkey &&typeof charSetRules[0] !== 'boolean';//0.3.4.不能连续有两个 Boolean 值isArgCharSetRulesOkey = isArgCharSetRulesOkey &&!charSetRules.some((x, idx) =>typeof x === 'boolean' &&typeof charSetRules[idx + 1] === 'boolean');//0.3.5.每一个名称,都必须是已经存在的var charSets = charSetRules.filter(x => typeof x === 'string').map((name, idx) => [name, idx, g.NamedCharSets.Get(name)]);let badNames = charSets.filter(item => item[2] === undefined).map(item => item[0]);let hasBadName = badNames.length !== 0;if (!isArgCharSetRulesOkey)throw new Error('参数 charSetRules 必须形如:' +'[NamedCharSet_Name1[, reverse1], ' +'NamedCharSet_Name2[,reverse2], ....]');if (hasBadName) {let msg = badNames.join('”, “');msg = `“${msg}” 这些名称不是已有的命名字符集的名称。\n` +`支持的命名字符集名称如下(每行是一个字符集):\n` +g.NamedCharSets.AllNames + '\n请仅使用如上已有的名称';throw new Error(msg);}}//1.将 charSetRules 中的名称转换成对应的对象charSets.forEach(item => charSetRules[item[1]] = item[2]);//2.按规则取得总的正则表达式对象let re = NamedCharSet.Combine(minTimes, maxTimes, ...charSetRules);re = new RegExp(re.source, 'g');//3.取得需要的匹配let matches = [...Array(token).keys()].map(i => re.exec(from));//4.按情况返回提取到的目标文本let match = matches[token - 1];if (match === null) return '';else return match[0];}/*按已命名的正则表达式提取从文本或单元格值中提取匹配的文本from : 文本或单元格,非文本会被自动转换成文本regExpName : 要使用的命名的正则表达式的名称return : String, 匹配到的文本*/function GetMatchByNamedRegExp(from, token, regExpName) {{//0.检验并处理参数//0.1.检验并处理 from 参数if (typeof from != 'string') {if (Object.prototype.toString.call(from).slice(8, -1) === 'Range')from = from.Value().toString();elsefrom = from.toString();}if (from.length === 0) return '';//0.2.检验并处理 regExpName 参数if (typeof regExpName !== 'string')throw new TypeError('参数 regExpName 必须是一个 String 对象');var nre = g.NamedRegExps.Get(regExpName);if (nre === undefined)throw new Error(`名为[${regExpName}]的命名正则表达式不存在。\n` +`您可以使用的命名正则表达式名称如下:\n${g.NamedRegExps.AllNames}`);//0.3.检验并处理 token 参数if (token === undefined) token = 1;if (typeof token != 'number' || Math.trunc(token) <= 0)throw new Error('参数 token 必须是正整数');elsetoken = Math.trunc(token);}//1.按规则取得总的正则表达式对象let re = new RegExp(nre.Expression, 'g');//2.取得需要的匹配let matches = [...Array(token).keys()].map(i => re.exec(from));//3.按情况返回提取到的目标文本let match = matches[token - 1];if (match === null) return '';else return match[0];}
应用示例:
| 样本 | 产生的值 | 使用的公式 |
|---|---|---|
| he is 24, and 65kg weight. | 65 | ‘=GetMatchByNamedCharSet(A1,2,0,0,”数字”) |
| he said: “中国,你好” | 你好 | ‘=GetMatchByNamedCharSet(A2,2,0,0,”中文”) |
| he said: “中国666,中文666” | 中文666 | ‘=GetMatchByNamedCharSet(A3,2,0,0,”数字”,”中文”) |
| he has two email:”1234@qq.com” and “hero666@sina.com | hero666@sina.com | ‘=GetMatchByNamedRegExp(A4,2,”邮箱”) |
| The life of sun is about 10 billion years,his weight is 1.98892E30 kg. | 1.98892E30 | ‘=GetMatchByNamedRegExp(A5,2,”数字”) |
三、依赖的类型
class NamedObject {constructor(...names) {if (!(names.length >= 1 && names.every(n => typeof n === 'string')))throw new TypeError('Parameter "names" must be a string ' +'array, and its length must great than 0.');this.Names = names;}/*判断当前对象是否拥有指定的名称name : String, 必选,要决断是否存在的名称return : Boolean,指示是否存在给定的名称,存在则为 true,否则为 false*/HasName(name) {name = name.toUpperCase();return this.Names.some(n => n.toUpperCase() === name);}/*为对象添加指定的名称names : String[],必选,要添加的名称*/AddName(...names) {if (names.length === 0) return;if (names.some(n => typeof n !== 'string'))throw new TypeError('Parameter "names" must be ' +'a string array.');names.forEach(name => {let upper = name.toUpperCase();let exist = this.Names.some(n =>n.toUpperCase() === upper);if (!exist) this.Names.push(name);});}/*为对象删除指定的名称names : String[],必选,要删除的名称*/RemoveName(...names) {if (names.length === 0) return;if (names.some(n => typeof n !== 'string'))throw new TypeError('Parameter "names" must be ' +'a string array.');let indices = names.map(name => {let upper = name.toUpperCase();let index = this.Names.findIndex(n =>n.toUpperCase() === upper);if (index === -1)throw new Error(`名称【${name}】不存在,删除失败`);return index;});indices.reverse().forEach(idx => this.Names.splice(idx, 1));}/*获取对象的名称数量*/get NameCount() {return this.Names.length;}toString() {return this.Names.join(',');}}class NamedObjectCollection {Get(name) {/*在当前集合中查找具有指定名称的命名对象name : String, 必选,要查找的命名对象的名称return : NamedCharSet/undefined,查找到的命名对象,如果未找到,则会返回 undefined*/return Object.keys(this).map(key => this[key]).filter(x => x.constructor !== Function.prototype.constructor).find(x => x.HasName(name));}get AllNames() {/*取得支持的所有名称*/return Object.keys(this).map(key => this[key]).filter(x => x.constructor !== Function.prototype.constructor).map(obj => NamedObject.prototype.toString.call(obj)).join('\n');}}/*命名的字符集正则*/class NamedCharSet extends NamedObject {constructor(expr, ...names) {super(...names);if (typeof expr != 'string')throw new TypeError('Parameter "expr" must be a String object.');this.Expression = expr;}/*以此字符集匹配规则为基础,获取匹配指定次数的正则表达式minTimes : 最小匹配次数;undefined/number,必须不小于 0maxTimes : 最大匹配次数;undefined/number,必须不小于 0reverse : Boolean,可选,默认为 false,为 true 时会反转本字符集return : RegExp,一个以此正则表达式为基础,指定了匹配次数范围的正则表达式对象;*/ToContinuous(minTimes, maxTimes, reverse = false) {let boundRule = NamedCharSet.ProcessBounds(minTimes, maxTimes);let reStr, re;if (boundRule === '') {if (reverse)return new RegExp(this.Reverse());elsereturn this.valueOf();} else {if (reverse)reStr = this.Reverse();elsereStr = this.toString();reStr += boundRule;return new RegExp(reStr);}}/*处理字符集匹配时的上下限minTimes : Number, 可选,匹配最低次数maxTimes : Number,可选,匹配最多次数return : Number[],形式是 [minTimes, maxTimes]*/static ProcessBounds(minTimes, maxTimes) {if (minTimes === undefined && maxTimes === undefined) return '';if (minTimes === undefined)minTimes = 0;else if(typeof minTimes === 'number' &&Math.trunc(minTimes) >= 0)minTimes = Math.trunc(minTimes);elsethrow new Error('Parameter "minTimes" can be default, '+'otherwise must be a number, and great or equal then 0.');let noMaxTimes = maxTimes === undefined;if (noMaxTimes)maxTimes = 0;else if(typeof maxTimes === 'number' &&Math.trunc(maxTimes) >= 0)maxTimes = Math.trunc(maxTimes);elsethrow new Error('Parameter "maxTimes" can be default, '+'otherwise must be a number, and great or equal then 0.');if (minTimes === 0 && maxTimes === 0) return '';if (maxTimes === 0) maxTimes = '';if (minTimes === 0) minTimes = '';return `{${minTimes},${maxTimes}}`;}/*将当前字符集匹配规则,与其它的字符集匹配规则拼接起来minTimes : Number, 可选,匹配最低次数maxTimes : Number,可选,匹配最多次数other : [NamedCharSet1 [, reverse1], NamedCharSet2[, reverse2],...]就是要追加的另一些 NamedCharSet 对象,及其是否反转的描述reverse : Boolean,可选,默认为 false,为 true 时会反转本字符集return : 合并后的正则表达式*/CombineWith(minTimes, maxTimes, reverse = false, ...other) {other.unshift(this, reverse);return NamedCharSet.Combine(minTimes, maxTimes, ...other);}/*将当前字符集匹配规则,与其它的字符集匹配规则拼接起来minTimes : Number, 可选,匹配最低次数maxTimes : Number,可选,匹配最多次数rules : [NamedCharSet1[, reverse1], NamedCharSet2[, reverse2], ...]就是要合并的一些 NamedCharSet 对象,及其是否反转的设定return : RegExp,合并后的正则表达式对象*/static Combine(minTimes, maxTimes, ...rules) {{//0.检验参数 rules 的合法性//0.1.必须最少有一个元素let isArgRulesOkey = rules.length > 0;//0.2.所有元素要么是 Boolean,要么是 NamedCharSet 对象isArgRulesOkey = isArgRulesOkey && rules.every(x =>x.constructor === NamedCharSet.prototype.constructor || typeof x === 'boolean');//0.3.第一个元素不能是 Boolean 值isArgRulesOkey = isArgRulesOkey &&typeof rules[0] !== 'boolean';//0.4.不能有连续的 Boolean 元素isArgRulesOkey = isArgRulesOkey && !rules.some((x, idx) => typeof x == 'boolean' &&typeof rules[idx + 1] == 'boolean');//0.5.如果参数不合法,报错并反馈信息if (!isArgRulesOkey)throw new TypeError('Parameter "rules" must be like ' +'[NamedCharSet1[, reverse1], NamedCharSet2[, reverse2], ...].');}//1.整理所有的字符集规则rules = rules.reduce((items, cur) => {let last = items[items.length - 1];if (typeof cur != typeof last)items.push(cur);elseitems.push(false, cur);return items;}, []);//2.按需要为最后一个规则增加是否反转的描述if (typeof rules[rules.length - 1] != 'boolean')rules.push(false);//3.将所有的字符集规则,转换成成对的规则rules = [...new Array(Math.trunc(rules.length / 2)).keys()].map(idx => [rules[idx * 2], rules[idx * 2 + 1]]);//4.取得所有的字符集规则合并后的正则表达式的字符串let rulesExpression;if (rules.length === 1) {let [ncs, reverse] = rules[0];if (reverse)rulesExpression = ncs.Reverse();elserulesExpression = ncs.toString();} else {rulesExpression = rules.map(rule => {if (rule[1]) return rule[0].Reverse();else return rule[0].toString();}).join('|');//最后的表达式形如:(charSet1|charSet2|...)rulesExpression = `(${rulesExpression})`;}//5.取得限定匹配次数上下限的正则表达式字符串let boundRule = NamedCharSet.ProcessBounds(minTimes, maxTimes);if (boundRule === '') boundRule = '+';return new RegExp(rulesExpression + boundRule);}//反转字符集Reverse() {if (this.Expression.startsWith('^'))return this.Expression.substr(1);elsereturn `[^${this.Expression}]`;}toString() {return `[${this.Expression}]`;}valueOf() {return new RegExp(this.toString());}}/*命名的功能正则表达式:面向各种固定格式字符串的正则规则*/class NamedRegExp extends NamedObject {constructor(expr, ...names) {super(...names);if (typeof expr != 'string')throw new TypeError('Parameter "expr" must be a String object.');this.Expression = expr;}//转换成匹配首尾的正则表达式ToWholeMode() {return new RegExp(`^${this.Expression}$`);}//取得当前正则表达式的表达式字符串toString() {return this.Expression;}//取得当前正则表达式的正则对象valueOf() {return new RegExp(this.toString());}}
四、依赖的对象
const g = {NamedCharSets : {Chinese : new NamedCharSet('\\u4e00-\\u9fbc', '汉语', '中文', '汉字','zh-cn', '中国字', 'cn', 'zh'),Number : new NamedCharSet('\\d', '数字', '整数', '数', '数符', '阿拉伯数字','Number', 'Integer', 'int', 'NumberChar'),BigLetter : new NamedCharSet('A-Z', '大写字母', 'Capital', 'CapitalLetter','BigLetter', 'LargeLetter', 'UpperCase'),SmallLetter : new NamedCharSet('a-z', '小写字母', 'SmallLetter', 'Minuscule','LowerCase'),Letter : new NamedCharSet('a-zA-Z','字母', 'Letter', 'Alpha'),NumberAndLetter : new NamedCharSet('a-zA-Z0-9', 'Number_Letter', 'numberandletter','number and letter'),NumberAndLeterAndUnderline : new NamedCharSet('a-zA-Z0-9_', '字母数字下划线', '标识符', 'Number and Letter and underline','NumberAndLetterAndUnderline', 'Number_Letter_Underline'),WhiteSpace : new NamedCharSet('\s','空白字符', 'white space', 'WhiteSpace'),},NamedRegExps : {//数字:支持正数、负数、科学计数Number: new NamedRegExp('(\\+|-|)\\d+(\\.\\d+)?((e|E)(\\+|-|)\\d+)?','数字', 'Number'),// Email地址:Email: new NamedRegExp('\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*','邮箱', '邮箱地址', '电子邮件', '电子邮件地址', '电子邮箱','电子邮箱地址', 'Email', 'email address'),// 护照:Passport: new NamedRegExp('1[45][0-9]{7}|G[0-9]{8}|P[0-9]{7}|S[0-9]{7,8}|D[0-9]+','护照', '护照号码', 'Passport', 'Passport Number'),// 手机号码:Mobile: new NamedRegExp('1[345789]\\d{9}', '手机号', '手机号码', 'Mobile','Mobile Number'),// 座机号码:Telephone: new NamedRegExp('0\\d{2,3}-\\d{5,9}|0\\d{2,3}-\\d{5,9}','固定电话', '座机', '电话号码', '座机号', 'Telephone', 'Telephone Number'),// 身份证号(15位、18位数字):IdCard: new NamedRegExp('([1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|' +'3[0-1])\\d{3})|([1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2' +']\\d)|3[0-1])((\\d{4})|\\d{3}[Xx]))', '身份证', '身份证号', 'IdCard','Id', 'Id card', 'id card number'),// 日期: 2017-1-1或2017/1/1Date: new NamedRegExp('\\d{4}(-|\\/)\\d{1,2}(-|\\/)\\d{1,2}', '日期', 'Date'),// 时间: 10:00:00Time: new NamedRegExp('(0?\d|1\d|2[0-3]):(0?\d|[1-5]\d):(0?\d|[1-5]\d)','时间', 'Time'),// 日期+时间: 2017-1-1 10:00:00DateTime: new NamedRegExp('\d{4}(-|\/)\d{1,2}(-|\/)\d{1,2}\s([01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d','日期时间', 'datetime', 'date and time', '日期和时间'),//腾讯QQ号:QQ: new NamedRegExp('[1-9][0-9]{4,}', 'QQ', 'QQ号', 'QQ号码'),//中国邮政编码:Postcode: new NamedRegExp('[1-9]\\d{5}(?!\\d)','邮政编码', '邮编', '邮编号码', 'Postcode'),//IP地址:IPv4: new NamedRegExp('\\d+\\.\\d+\\.\\d+\\.\\d+', 'IPv4', 'IPv4地址'),//域名:URL: new NamedRegExp('(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?' +'[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]' +'{0,62})+(:\d+)*(\/\w+\.\w+)*([\?&]\w+=\w*)*', 'URL', '域名'),// 车牌:CarNumber: new NamedRegExp('[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣' +'鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9' +']{4}[A-Z0-9挂学警港澳]{1}', '车牌', '车牌号', 'CarNumber','Car Number', 'license plate number', 'license plate'),//银行卡:BankCardNumber: new NamedRegExp('[1-9]{1}(\d{15}|\d{18})','银行卡号', 'Bank card number', 'BankCardNumber'),},};//为一些属性对象重置原型,通过这种方式为其附加一些有用的方法和属性Object.setPrototypeOf(g.NamedCharSets, NamedObjectCollection.prototype);Object.setPrototypeOf(g.NamedRegExps, NamedObjectCollection.prototype);
