一、主要的类型与对象之间的关系:
二、自定义的公式函数(应用)
/*提取文本中连续匹配指定的字符集 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();
else
from = 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 必须是正整数');
else
token = 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();
else
from = 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 必须是正整数');
else
token = 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,必须不小于 0
maxTimes : 最大匹配次数;undefined/number,必须不小于 0
reverse : 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());
else
return this.valueOf();
} else {
if (reverse)
reStr = this.Reverse();
else
reStr = 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);
else
throw 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);
else
throw 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);
else
items.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();
else
rulesExpression = 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);
else
return `[^${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/1
Date: new NamedRegExp('\\d{4}(-|\\/)\\d{1,2}(-|\\/)\\d{1,2}', '日期', 'Date'),
// 时间: 10:00:00
Time: 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:00
DateTime: 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);