一、主要的类型与对象之间的关系:

WPS JSA 宏代码:1.按命名的正则或字符集提取文本 - 图1

二、自定义的公式函数(应用)

  1. /*提取文本中连续匹配指定的字符集 N-M 次之间的第 x 个匹配到的连续字符串
  2. from : 文本或单元格,非文本会被自动转换成文本
  3. token : 提取第几个这种连续字符串,必须是正整数
  4. charSetRules : [NamedCharSet_Name1[, reverse1],
  5. NamedCharSet_Name2[,reverse2], ....]
  6. NamedCharSet_Name1/2/.../N : 第 N 个命名字符集的名称
  7. reverse1/2/.../N : 与上面字符集名称一一对应的字符集是否反转的描述,它们为
  8. true 时,它对应的字符集将被反转后,再进行匹配;如果省略,一律设为 false,
  9. 即不反转字符集
  10. return : String,提取到的文本*/
  11. function GetMatchByNamedCharSet(from, token,
  12. minTimes, maxTimes, ...charSetRules) {
  13. {//0.检验并处理参数
  14. //0.1.检验并处理 from 参数
  15. if (typeof from != 'string') {
  16. if (Object.prototype.toString.call(from)
  17. .slice(8, -1) === 'Range')
  18. from = from.Value().toString();
  19. else
  20. from = from.toString();
  21. }
  22. if (from.length === 0) return '';
  23. //0.2.检验并处理 token 参数
  24. if (token === undefined) token = 1;
  25. if (typeof token != 'number' || Math.trunc(token) <= 0)
  26. throw new Error('参数 token 必须是正整数');
  27. else
  28. token = Math.trunc(token);
  29. //0.3.检验并处理 charSetRules 参数
  30. //0.3.1.至少应该含有一个元素
  31. let isArgCharSetRulesOkey = charSetRules.length > 0;
  32. //0.3.2.只允许包含 String/Boolean 元素
  33. isArgCharSetRulesOkey = isArgCharSetRulesOkey &&
  34. charSetRules.every(x =>
  35. typeof x === 'boolean' ||
  36. typeof x === 'string');
  37. //0.3.3.第一个元素不能是 Boolean 值
  38. isArgCharSetRulesOkey = isArgCharSetRulesOkey &&
  39. typeof charSetRules[0] !== 'boolean';
  40. //0.3.4.不能连续有两个 Boolean 值
  41. isArgCharSetRulesOkey = isArgCharSetRulesOkey &&
  42. !charSetRules.some((x, idx) =>
  43. typeof x === 'boolean' &&
  44. typeof charSetRules[idx + 1] === 'boolean');
  45. //0.3.5.每一个名称,都必须是已经存在的
  46. var charSets = charSetRules.filter(x => typeof x === 'string')
  47. .map((name, idx) => [name, idx, g.NamedCharSets.Get(name)]);
  48. let badNames = charSets.filter(item => item[2] === undefined)
  49. .map(item => item[0]);
  50. let hasBadName = badNames.length !== 0;
  51. if (!isArgCharSetRulesOkey)
  52. throw new Error('参数 charSetRules 必须形如:' +
  53. '[NamedCharSet_Name1[, reverse1], ' +
  54. 'NamedCharSet_Name2[,reverse2], ....]');
  55. if (hasBadName) {
  56. let msg = badNames.join('”, “');
  57. msg = `“${msg}” 这些名称不是已有的命名字符集的名称。\n` +
  58. `支持的命名字符集名称如下(每行是一个字符集):\n` +
  59. g.NamedCharSets.AllNames + '\n请仅使用如上已有的名称';
  60. throw new Error(msg);
  61. }
  62. }
  63. //1.将 charSetRules 中的名称转换成对应的对象
  64. charSets.forEach(item => charSetRules[item[1]] = item[2]);
  65. //2.按规则取得总的正则表达式对象
  66. let re = NamedCharSet.Combine(
  67. minTimes, maxTimes, ...charSetRules);
  68. re = new RegExp(re.source, 'g');
  69. //3.取得需要的匹配
  70. let matches = [...Array(token).keys()]
  71. .map(i => re.exec(from));
  72. //4.按情况返回提取到的目标文本
  73. let match = matches[token - 1];
  74. if (match === null) return '';
  75. else return match[0];
  76. }
  77. /*按已命名的正则表达式提取从文本或单元格值中提取匹配的文本
  78. from : 文本或单元格,非文本会被自动转换成文本
  79. regExpName : 要使用的命名的正则表达式的名称
  80. return : String, 匹配到的文本*/
  81. function GetMatchByNamedRegExp(from, token, regExpName) {
  82. {//0.检验并处理参数
  83. //0.1.检验并处理 from 参数
  84. if (typeof from != 'string') {
  85. if (Object.prototype.toString.call(from)
  86. .slice(8, -1) === 'Range')
  87. from = from.Value().toString();
  88. else
  89. from = from.toString();
  90. }
  91. if (from.length === 0) return '';
  92. //0.2.检验并处理 regExpName 参数
  93. if (typeof regExpName !== 'string')
  94. throw new TypeError('参数 regExpName 必须是一个 String 对象');
  95. var nre = g.NamedRegExps.Get(regExpName);
  96. if (nre === undefined)
  97. throw new Error(`名为[${regExpName}]的命名正则表达式不存在。\n` +
  98. `您可以使用的命名正则表达式名称如下:\n${g.NamedRegExps.AllNames}`);
  99. //0.3.检验并处理 token 参数
  100. if (token === undefined) token = 1;
  101. if (typeof token != 'number' || Math.trunc(token) <= 0)
  102. throw new Error('参数 token 必须是正整数');
  103. else
  104. token = Math.trunc(token);
  105. }
  106. //1.按规则取得总的正则表达式对象
  107. let re = new RegExp(nre.Expression, 'g');
  108. //2.取得需要的匹配
  109. let matches = [...Array(token).keys()]
  110. .map(i => re.exec(from));
  111. //3.按情况返回提取到的目标文本
  112. let match = matches[token - 1];
  113. if (match === null) return '';
  114. else return match[0];
  115. }

应用示例:

样本 产生的值 使用的公式
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,”数字”)

三、依赖的类型

  1. class NamedObject {
  2. constructor(...names) {
  3. if (!(names.length >= 1 && names.every(n => typeof n === 'string')))
  4. throw new TypeError('Parameter "names" must be a string ' +
  5. 'array, and its length must great than 0.');
  6. this.Names = names;
  7. }
  8. /*判断当前对象是否拥有指定的名称
  9. name : String, 必选,要决断是否存在的名称
  10. return : Boolean,指示是否存在给定的名称,存在则为 true,否则为 false*/
  11. HasName(name) {
  12. name = name.toUpperCase();
  13. return this.Names.some(n => n.toUpperCase() === name);
  14. }
  15. /*为对象添加指定的名称
  16. names : String[],必选,要添加的名称*/
  17. AddName(...names) {
  18. if (names.length === 0) return;
  19. if (names.some(n => typeof n !== 'string'))
  20. throw new TypeError('Parameter "names" must be ' +
  21. 'a string array.');
  22. names.forEach(name => {
  23. let upper = name.toUpperCase();
  24. let exist = this.Names.some(n =>
  25. n.toUpperCase() === upper);
  26. if (!exist) this.Names.push(name);
  27. });
  28. }
  29. /*为对象删除指定的名称
  30. names : String[],必选,要删除的名称*/
  31. RemoveName(...names) {
  32. if (names.length === 0) return;
  33. if (names.some(n => typeof n !== 'string'))
  34. throw new TypeError('Parameter "names" must be ' +
  35. 'a string array.');
  36. let indices = names.map(name => {
  37. let upper = name.toUpperCase();
  38. let index = this.Names.findIndex(n =>
  39. n.toUpperCase() === upper);
  40. if (index === -1)
  41. throw new Error(`名称【${name}】不存在,删除失败`);
  42. return index;
  43. });
  44. indices.reverse()
  45. .forEach(idx => this.Names.splice(idx, 1));
  46. }
  47. /*获取对象的名称数量*/
  48. get NameCount() {
  49. return this.Names.length;
  50. }
  51. toString() {
  52. return this.Names.join(',');
  53. }
  54. }
  55. class NamedObjectCollection {
  56. Get(name) {
  57. /*在当前集合中查找具有指定名称的命名对象
  58. name : String, 必选,要查找的命名对象的名称
  59. return : NamedCharSet/undefined,查找到的命名对象,如果
  60. 未找到,则会返回 undefined*/
  61. return Object.keys(this)
  62. .map(key => this[key])
  63. .filter(x => x.constructor !== Function.prototype.constructor)
  64. .find(x => x.HasName(name));
  65. }
  66. get AllNames() {
  67. /*取得支持的所有名称*/
  68. return Object.keys(this)
  69. .map(key => this[key])
  70. .filter(x => x.constructor !== Function.prototype.constructor)
  71. .map(obj => NamedObject.prototype.toString.call(obj))
  72. .join('\n');
  73. }
  74. }
  75. /*命名的字符集正则*/
  76. class NamedCharSet extends NamedObject {
  77. constructor(expr, ...names) {
  78. super(...names);
  79. if (typeof expr != 'string')
  80. throw new TypeError('Parameter "expr" must be a String object.');
  81. this.Expression = expr;
  82. }
  83. /*以此字符集匹配规则为基础,获取匹配指定次数的正则表达式
  84. minTimes : 最小匹配次数;undefined/number,必须不小于 0
  85. maxTimes : 最大匹配次数;undefined/number,必须不小于 0
  86. reverse : Boolean,可选,默认为 false,为 true 时会反转本字符集
  87. return : RegExp,一个以此正则表达式为基础,指定了匹配次数范围的正则表达式对象;
  88. */
  89. ToContinuous(minTimes, maxTimes, reverse = false) {
  90. let boundRule = NamedCharSet
  91. .ProcessBounds(minTimes, maxTimes);
  92. let reStr, re;
  93. if (boundRule === '') {
  94. if (reverse)
  95. return new RegExp(this.Reverse());
  96. else
  97. return this.valueOf();
  98. } else {
  99. if (reverse)
  100. reStr = this.Reverse();
  101. else
  102. reStr = this.toString();
  103. reStr += boundRule;
  104. return new RegExp(reStr);
  105. }
  106. }
  107. /*处理字符集匹配时的上下限
  108. minTimes : Number, 可选,匹配最低次数
  109. maxTimes : Number,可选,匹配最多次数
  110. return : Number[],形式是 [minTimes, maxTimes]*/
  111. static ProcessBounds(minTimes, maxTimes) {
  112. if (minTimes === undefined && maxTimes === undefined) return '';
  113. if (minTimes === undefined)
  114. minTimes = 0;
  115. else if(typeof minTimes === 'number' &&
  116. Math.trunc(minTimes) >= 0)
  117. minTimes = Math.trunc(minTimes);
  118. else
  119. throw new Error('Parameter "minTimes" can be default, '+
  120. 'otherwise must be a number, and great or equal then 0.');
  121. let noMaxTimes = maxTimes === undefined;
  122. if (noMaxTimes)
  123. maxTimes = 0;
  124. else if(typeof maxTimes === 'number' &&
  125. Math.trunc(maxTimes) >= 0)
  126. maxTimes = Math.trunc(maxTimes);
  127. else
  128. throw new Error('Parameter "maxTimes" can be default, '+
  129. 'otherwise must be a number, and great or equal then 0.');
  130. if (minTimes === 0 && maxTimes === 0) return '';
  131. if (maxTimes === 0) maxTimes = '';
  132. if (minTimes === 0) minTimes = '';
  133. return `{${minTimes},${maxTimes}}`;
  134. }
  135. /*将当前字符集匹配规则,与其它的字符集匹配规则拼接起来
  136. minTimes : Number, 可选,匹配最低次数
  137. maxTimes : Number,可选,匹配最多次数
  138. other : [NamedCharSet1 [, reverse1], NamedCharSet2[, reverse2],...]
  139. 就是要追加的另一些 NamedCharSet 对象,及其是否反转的描述
  140. reverse : Boolean,可选,默认为 false,为 true 时会反转本字符集
  141. return : 合并后的正则表达式*/
  142. CombineWith(minTimes, maxTimes, reverse = false, ...other) {
  143. other.unshift(this, reverse);
  144. return NamedCharSet.Combine(minTimes, maxTimes, ...other);
  145. }
  146. /*将当前字符集匹配规则,与其它的字符集匹配规则拼接起来
  147. minTimes : Number, 可选,匹配最低次数
  148. maxTimes : Number,可选,匹配最多次数
  149. rules : [NamedCharSet1[, reverse1], NamedCharSet2[, reverse2], ...]
  150. 就是要合并的一些 NamedCharSet 对象,及其是否反转的设定
  151. return : RegExp,合并后的正则表达式对象*/
  152. static Combine(minTimes, maxTimes, ...rules) {
  153. {//0.检验参数 rules 的合法性
  154. //0.1.必须最少有一个元素
  155. let isArgRulesOkey = rules.length > 0;
  156. //0.2.所有元素要么是 Boolean,要么是 NamedCharSet 对象
  157. isArgRulesOkey = isArgRulesOkey && rules.every(x =>
  158. x.constructor === NamedCharSet.prototype
  159. .constructor || typeof x === 'boolean');
  160. //0.3.第一个元素不能是 Boolean 值
  161. isArgRulesOkey = isArgRulesOkey &&
  162. typeof rules[0] !== 'boolean';
  163. //0.4.不能有连续的 Boolean 元素
  164. isArgRulesOkey = isArgRulesOkey && !rules
  165. .some((x, idx) => typeof x == 'boolean' &&
  166. typeof rules[idx + 1] == 'boolean');
  167. //0.5.如果参数不合法,报错并反馈信息
  168. if (!isArgRulesOkey)
  169. throw new TypeError('Parameter "rules" must be like ' +
  170. '[NamedCharSet1[, reverse1], NamedCharSet2[, reverse2], ...].');
  171. }
  172. //1.整理所有的字符集规则
  173. rules = rules.reduce((items, cur) => {
  174. let last = items[items.length - 1];
  175. if (typeof cur != typeof last)
  176. items.push(cur);
  177. else
  178. items.push(false, cur);
  179. return items;
  180. }, []);
  181. //2.按需要为最后一个规则增加是否反转的描述
  182. if (typeof rules[rules.length - 1] != 'boolean')
  183. rules.push(false);
  184. //3.将所有的字符集规则,转换成成对的规则
  185. rules = [...new Array(Math.trunc(rules.length / 2)).keys()]
  186. .map(idx => [rules[idx * 2], rules[idx * 2 + 1]]);
  187. //4.取得所有的字符集规则合并后的正则表达式的字符串
  188. let rulesExpression;
  189. if (rules.length === 1) {
  190. let [ncs, reverse] = rules[0];
  191. if (reverse)
  192. rulesExpression = ncs.Reverse();
  193. else
  194. rulesExpression = ncs.toString();
  195. } else {
  196. rulesExpression = rules.map(rule => {
  197. if (rule[1]) return rule[0].Reverse();
  198. else return rule[0].toString();
  199. }).join('|');
  200. //最后的表达式形如:(charSet1|charSet2|...)
  201. rulesExpression = `(${rulesExpression})`;
  202. }
  203. //5.取得限定匹配次数上下限的正则表达式字符串
  204. let boundRule = NamedCharSet
  205. .ProcessBounds(minTimes, maxTimes);
  206. if (boundRule === '') boundRule = '+';
  207. return new RegExp(rulesExpression + boundRule);
  208. }
  209. //反转字符集
  210. Reverse() {
  211. if (this.Expression.startsWith('^'))
  212. return this.Expression.substr(1);
  213. else
  214. return `[^${this.Expression}]`;
  215. }
  216. toString() {
  217. return `[${this.Expression}]`;
  218. }
  219. valueOf() {
  220. return new RegExp(this.toString());
  221. }
  222. }
  223. /*命名的功能正则表达式:面向各种固定格式字符串的正则规则*/
  224. class NamedRegExp extends NamedObject {
  225. constructor(expr, ...names) {
  226. super(...names);
  227. if (typeof expr != 'string')
  228. throw new TypeError('Parameter "expr" must be a String object.');
  229. this.Expression = expr;
  230. }
  231. //转换成匹配首尾的正则表达式
  232. ToWholeMode() {
  233. return new RegExp(`^${this.Expression}$`);
  234. }
  235. //取得当前正则表达式的表达式字符串
  236. toString() {
  237. return this.Expression;
  238. }
  239. //取得当前正则表达式的正则对象
  240. valueOf() {
  241. return new RegExp(this.toString());
  242. }
  243. }

四、依赖的对象

  1. const g = {
  2. NamedCharSets : {
  3. Chinese : new NamedCharSet(
  4. '\\u4e00-\\u9fbc', '汉语', '中文', '汉字',
  5. 'zh-cn', '中国字', 'cn', 'zh'),
  6. Number : new NamedCharSet(
  7. '\\d', '数字', '整数', '数', '数符', '阿拉伯数字',
  8. 'Number', 'Integer', 'int', 'NumberChar'),
  9. BigLetter : new NamedCharSet(
  10. 'A-Z', '大写字母', 'Capital', 'CapitalLetter',
  11. 'BigLetter', 'LargeLetter', 'UpperCase'),
  12. SmallLetter : new NamedCharSet(
  13. 'a-z', '小写字母', 'SmallLetter', 'Minuscule',
  14. 'LowerCase'),
  15. Letter : new NamedCharSet('a-zA-Z',
  16. '字母', 'Letter', 'Alpha'),
  17. NumberAndLetter : new NamedCharSet(
  18. 'a-zA-Z0-9', 'Number_Letter', 'numberandletter',
  19. 'number and letter'),
  20. NumberAndLeterAndUnderline : new NamedCharSet(
  21. 'a-zA-Z0-9_', '字母数字下划线', '标识符', 'Number and Letter and underline',
  22. 'NumberAndLetterAndUnderline', 'Number_Letter_Underline'),
  23. WhiteSpace : new NamedCharSet('\s',
  24. '空白字符', 'white space', 'WhiteSpace'),
  25. },
  26. NamedRegExps : {
  27. //数字:支持正数、负数、科学计数
  28. Number: new NamedRegExp('(\\+|-|)\\d+(\\.\\d+)?((e|E)(\\+|-|)\\d+)?',
  29. '数字', 'Number'),
  30. // Email地址:
  31. Email: new NamedRegExp('\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*',
  32. '邮箱', '邮箱地址', '电子邮件', '电子邮件地址', '电子邮箱',
  33. '电子邮箱地址', 'Email', 'email address'),
  34. // 护照:
  35. Passport: new NamedRegExp('1[45][0-9]{7}|G[0-9]{8}|P[0-9]{7}|S[0-9]{7,8}|D[0-9]+',
  36. '护照', '护照号码', 'Passport', 'Passport Number'),
  37. // 手机号码:
  38. Mobile: new NamedRegExp('1[345789]\\d{9}', '手机号', '手机号码', 'Mobile',
  39. 'Mobile Number'),
  40. // 座机号码:
  41. Telephone: new NamedRegExp('0\\d{2,3}-\\d{5,9}|0\\d{2,3}-\\d{5,9}',
  42. '固定电话', '座机', '电话号码', '座机号', 'Telephone', 'Telephone Number'),
  43. // 身份证号(15位、18位数字):
  44. IdCard: new NamedRegExp('([1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|' +
  45. '3[0-1])\\d{3})|([1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2' +
  46. ']\\d)|3[0-1])((\\d{4})|\\d{3}[Xx]))', '身份证', '身份证号', 'IdCard',
  47. 'Id', 'Id card', 'id card number'),
  48. // 日期: 2017-1-1或2017/1/1
  49. Date: new NamedRegExp('\\d{4}(-|\\/)\\d{1,2}(-|\\/)\\d{1,2}', '日期', 'Date'),
  50. // 时间: 10:00:00
  51. Time: new NamedRegExp('(0?\d|1\d|2[0-3]):(0?\d|[1-5]\d):(0?\d|[1-5]\d)',
  52. '时间', 'Time'),
  53. // 日期+时间: 2017-1-1 10:00:00
  54. DateTime: new NamedRegExp('\d{4}(-|\/)\d{1,2}(-|\/)\d{1,2}\s([01]?\d|2[0-3]):[0-5]?\d:[0-5]?\d',
  55. '日期时间', 'datetime', 'date and time', '日期和时间'),
  56. //腾讯QQ号:
  57. QQ: new NamedRegExp('[1-9][0-9]{4,}', 'QQ', 'QQ号', 'QQ号码'),
  58. //中国邮政编码:
  59. Postcode: new NamedRegExp('[1-9]\\d{5}(?!\\d)',
  60. '邮政编码', '邮编', '邮编号码', 'Postcode'),
  61. //IP地址:
  62. IPv4: new NamedRegExp('\\d+\\.\\d+\\.\\d+\\.\\d+', 'IPv4', 'IPv4地址'),
  63. //域名:
  64. URL: new NamedRegExp('(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?' +
  65. '[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]' +
  66. '{0,62})+(:\d+)*(\/\w+\.\w+)*([\?&]\w+=\w*)*', 'URL', '域名'),
  67. // 车牌:
  68. CarNumber: new NamedRegExp('[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣' +
  69. '鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9' +
  70. ']{4}[A-Z0-9挂学警港澳]{1}', '车牌', '车牌号', 'CarNumber',
  71. 'Car Number', 'license plate number', 'license plate'),
  72. //银行卡:
  73. BankCardNumber: new NamedRegExp('[1-9]{1}(\d{15}|\d{18})',
  74. '银行卡号', 'Bank card number', 'BankCardNumber'),
  75. },
  76. };
  77. //为一些属性对象重置原型,通过这种方式为其附加一些有用的方法和属性
  78. Object.setPrototypeOf(g.NamedCharSets, NamedObjectCollection.prototype);
  79. Object.setPrototypeOf(g.NamedRegExps, NamedObjectCollection.prototype);