exRegExp.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /**
  2. * 字符串分段处理
  3. * @param {String} code 处理字符串
  4. * @param {Array} options 分段数组
  5. * @param {Array<Function|Boolean>} maps 分段映射函数
  6. * @param {Boolean} all 是否完全映射
  7. * @returns {Array} 映射结果
  8. */
  9. function tsCode(code, options, maps, all) {
  10. let start = 0, res = [];
  11. if (maps) {
  12. if (typeof maps === 'function') {
  13. options.forEach((part, index) => {
  14. res.push(maps(code.slice(start, part + start)));
  15. start += part;
  16. });
  17. } else {
  18. options.forEach((part, index) => {
  19. if (typeof maps[index] === 'function') {
  20. res.push(maps[index](code.slice(start, part + start)));
  21. } else if (maps[index] !== undefined) {
  22. if (maps[index] === true) res.push(code.slice(start, part + start));
  23. else res.push(maps[index]);
  24. } else if (all) res.push(code.slice(start, part + start));
  25. start += part;
  26. });
  27. };
  28. return res
  29. } else {
  30. options.forEach(part => {
  31. res.push(code.slice(start, part + start));
  32. start += part;
  33. });
  34. return res
  35. }
  36. }
  37. function range(end, start = 0, reverse) {
  38. if (reverse) {
  39. const arr = [];
  40. for (let i = end; i >= start; i--) {
  41. arr.push(i);
  42. };
  43. return arr;
  44. } else {
  45. const arr = [];
  46. for (let i = start; i <= end; i++) {
  47. arr.push(i);
  48. };
  49. return arr;
  50. }
  51. }
  52. /**
  53. * 正则函数调用及函数结果插入递归替换
  54. * [[mark=func:regExp]] //正则函数调用(标记与函数不同名)
  55. * [[func:regExp]] //正则函数调用(标记与函数同名)
  56. * [[mark|func]] //按标记插入结果
  57. * @param {RegExp} reg
  58. */
  59. function splitRegExp({ oldRes, source, flags, split, res, fns, str, old, lastIndex, oldReg, oldLeft, oldRight, inner, oldSource }) {
  60. function next() {//回溯处理
  61. const oldData = old[old.length - 1];
  62. if (oldData) {
  63. const lefts = res[oldData.oldLeft];
  64. if (Array.isArray(lefts)) {
  65. return splitRegExp({ oldRes, flags, split, fns, str, old, ...oldData });
  66. } else {
  67. return splitRegExp({ oldRes, flags, split, fns, str, old, ...old.pop() });
  68. }
  69. }
  70. };
  71. if (oldReg) {//回溯处理
  72. let oldNum, leftRes;
  73. if (Array.isArray(res[oldLeft])) {
  74. res[oldLeft].shift();
  75. leftRes = oldRes[oldLeft] = res[oldLeft][0];
  76. if (res[oldLeft].reg[0]) {
  77. res[oldLeft].reg.shift();
  78. inner = res[oldLeft].reg[0];
  79. };
  80. if (leftRes.length === 1) {
  81. oldNum = 0;
  82. }
  83. } else {
  84. oldNum = 1;
  85. }
  86. let reg = inner ? (
  87. source = tsCode(oldSource, [lastIndex, res[oldLeft].len, Infinity], {
  88. 1: `(?:${inner})` //从结果池中以标记为键读取插入到原始正则之中
  89. }, true).join(''),
  90. new RegExp(inner, flags)
  91. ) : oldReg,
  92. mats = reg.exec(str);
  93. switch (oldNum) {
  94. case 0:
  95. old[old.length - 1] = { lastIndex, oldSource, source, oldReg: reg, oldLeft, oldRight, res: { ...res, [oldLeft]: leftRes }, inner };
  96. break;
  97. case 1:
  98. res[oldLeft] = fns[oldRight](mats, reg);
  99. old.push({ lastIndex, oldSource, source, oldReg: reg, oldLeft, oldRight, res: { ...res } });
  100. break;
  101. };
  102. return splitRegExp({ oldRes, source, flags, split, res, fns, str, old });
  103. } else {
  104. const mat = split.exec(source);//匹配函数调用点或函数结果插入点
  105. if (mat) {
  106. let lastIndex = mat.index, so = mat[1], index = so.indexOf(':');
  107. if (index !== -1) {//函数调用点
  108. let [first, last] = tsCode(so, [index, 1, Infinity], {
  109. 0: true,
  110. 2: true
  111. }),
  112. reg = new RegExp(last, flags),
  113. mats = reg.exec(str);
  114. if (mats) {//匹配成功递归处理
  115. const index2 = first.indexOf('='),
  116. [left, right] = index2 === -1 ? [first, first] : tsCode(first, [index2, 1, Infinity], {
  117. 0: true,
  118. 2: true
  119. }), fnRes = fns[right](mats, reg);
  120. res[left] = fnRes;
  121. if (Array.isArray(fnRes)) {
  122. fnRes.len = mat[0].length;
  123. if (fnRes.reg[0]) last = fnRes.reg[0];
  124. oldRes[left] = fnRes[0];
  125. };
  126. const oldSource = source;
  127. source = tsCode(source, [lastIndex, mat[0].length, Infinity], {
  128. 1: `(?:${last})` //从结果池中以标记为键读取插入到原始正则之中
  129. }, true).join('');
  130. old.push({ lastIndex, oldSource, source, oldReg: reg, oldLeft: left, oldRight: right, res: { ...res } });
  131. return splitRegExp({ oldRes, source, flags, split, res, fns, str, old });
  132. } else return next();
  133. } else {//函数结果插入点
  134. const [lenStr, name] = mat;
  135. let inner;
  136. if (res[name] === undefined) {
  137. try {
  138. inner = fns[name]();
  139. if (inner === undefined) return next();
  140. } catch (e) {
  141. return //函数中主动抛出错误,触发匹配失败返回
  142. }
  143. } else if (Array.isArray(res[name])) {
  144. inner = oldRes[name];
  145. } else {
  146. inner = res[name]
  147. };
  148. source = tsCode(source, [lastIndex, lenStr.length, Infinity], {
  149. 1: inner //从结果池中以标记为键读取插入到原始正则之中
  150. }, true).join('');
  151. return splitRegExp({ oldRes, source, flags, split, res, fns, str, old });
  152. }
  153. } else {
  154. return { source, next }
  155. };
  156. };
  157. };
  158. /**
  159. * 增强正则工具对象生成
  160. * @param {RegExp} reg 增强型原始正则
  161. * @param {Function} fns 插入正则的函数集
  162. * @param {String|Function} str 待匹配字符串,或生成下一个正则时回调执行函数
  163. * @param {Function} next 生成下一个正则时回调执行函数
  164. * @returns {{regExp:RegExp,next:Function,exec:Function,test:Function,match:Function,matchAll:Function,replace:Function,search:Function,split:Function}} 正则工具对象
  165. */
  166. function exRegExp({ reg, fns, str, next }) {
  167. const oldSplit = /\[\[(.*?[^\\])\]\]/g;
  168. let regExp, notFirst, source, flags, newFlags, nextCall, res = {}, oldRes = {}, old = [];
  169. if (reg) {
  170. source = reg.source.replace(/\\\[(?=\[)/g, '(?:\\[)');
  171. if (!(newFlags = reg.flags.replace('y', 'g')).includes('g')) {
  172. newFlags += 'g';
  173. };
  174. if (str) regExp = returnRegExp(oldRes, source, oldSplit, newFlags, fns, str, res, old);
  175. };
  176. function matRegExp(mat) {
  177. if (mat) {
  178. const { source, next: callback } = mat,
  179. regExp = new RegExp(source, flags);
  180. if (next) {
  181. nextCall = () => {
  182. const genReg = matRegExp(callback());
  183. next(genReg);
  184. return genReg;
  185. };
  186. } else {
  187. nextCall = () => matRegExp(callback());
  188. }
  189. return regExp;
  190. } else {
  191. //正则表达式出错
  192. throw { reg, flags, fns, str }
  193. };
  194. };
  195. function returnRegExp(oldRes, source, split, newFlags, fns, str, res, old, inner) {
  196. return matRegExp(splitRegExp({
  197. source,
  198. split,
  199. flags: newFlags,
  200. fns,
  201. str,
  202. res,
  203. old,
  204. inner,
  205. oldRes
  206. }));
  207. };
  208. function preExec() {
  209. if (notFirst) {
  210. try {
  211. regExp = nextCall();
  212. } catch (e) {
  213. return true
  214. };
  215. } else {
  216. notFirst = true;
  217. }
  218. };
  219. function exec() {
  220. if (preExec()) return null;
  221. const exec = regExp.exec(str);
  222. if (exec) {
  223. return exec
  224. } else {
  225. try {
  226. regExp = nextCall();
  227. return exec();
  228. } catch (e) {
  229. console.log(e);
  230. return null
  231. }
  232. }
  233. };
  234. function test() {
  235. const testRes = regExp.test(str);
  236. if (testRes) {
  237. return true
  238. } else {
  239. try {
  240. regExp = nextCall();
  241. return test();
  242. } catch (e) {
  243. console.log(e);
  244. return false
  245. }
  246. }
  247. };
  248. function match() {
  249. const matchRes = str.match(regExp);
  250. if (matchRes) {
  251. return matchRes
  252. } else {
  253. try {
  254. regExp = nextCall();
  255. return match();
  256. } catch (e) {
  257. console.log(e);
  258. return null
  259. }
  260. }
  261. };
  262. function matchAll() {
  263. const matchAllRes = str.matchAll(regExp);
  264. if (matchAllRes) {
  265. return matchAllRes
  266. } else {
  267. try {
  268. regExp = nextCall();
  269. return matchAll();
  270. } catch (e) {
  271. console.log(e);
  272. return null
  273. }
  274. }
  275. };
  276. function replace(callback, change) {
  277. const replaceRes = str.replace(regExp, callback);
  278. if (change) {
  279. if (replaceRes !== str) {
  280. return replaceRes
  281. } else {
  282. try {
  283. regExp = nextCall();
  284. return replace(str);
  285. } catch (e) {
  286. console.log(e);
  287. return str
  288. }
  289. };
  290. } else {
  291. return replaceRes
  292. };
  293. };
  294. function search() {
  295. const searchRes = str.search(regExp);
  296. if (searchRes !== -1) {
  297. return searchRes
  298. } else {
  299. try {
  300. regExp = nextCall();
  301. return search(str);
  302. } catch (e) {
  303. console.log(e);
  304. return -1
  305. }
  306. };
  307. };
  308. function split(limit, change) {
  309. const splitRes = str.split(regExp, limit);
  310. if (change) {
  311. if (splitRes.length !== 1) {
  312. return splitRes
  313. } else {
  314. try {
  315. regExp = nextCall();
  316. return split(str);
  317. } catch (e) {
  318. console.log(e);
  319. return [str]
  320. }
  321. };
  322. } else {
  323. return splitRes
  324. };
  325. };
  326. return {
  327. //当前生成的正则
  328. get regExp() { return regExp },
  329. set regExp(nv) {
  330. reg = nv;
  331. notFirst = false;
  332. res = {};
  333. oldRes = {};
  334. old = [];
  335. flags = reg.flags;
  336. nextCall = undefined;
  337. source = reg.source.replace(/\\\[(?=\[)/g, '(?:\\[)');
  338. if (!(newFlags = flags.replace('y', 'g')).includes('g')) {
  339. newFlags += 'g';
  340. };
  341. if (str) regExp = returnRegExp(oldRes, source, oldSplit, newFlags, fns, str, res, old);
  342. },
  343. //当前匹配的字符串
  344. get str() {
  345. return str
  346. },
  347. set str(newStr) {
  348. str = newStr;
  349. notFirst = false;
  350. res = {};
  351. oldRes = {};
  352. old = [];
  353. nextCall = undefined;
  354. if (reg) regExp = returnRegExp(oldRes, source, oldSplit, newFlags, fns, str, res, old);
  355. },
  356. next() {//生成下一个有效正则,全部无效时报错
  357. return nextCall()
  358. },
  359. exec, /**
  360. exec() => regExp.exec(str)
  361. 无需主动执行next,当上个正则耗尽后,再调用exec则主动生成下一个有效正则
  362. */
  363. test,/**
  364. test() => regExp.test(str)
  365. 匹配失败时主动执行next生成下一个有效正则
  366. 可主动执行next,主动生成下一个有效正则作为匹配正则
  367. */
  368. match,/**
  369. * match() => str.match(regExp)
  370. 匹配失败时主动执行next生成下一个有效正则
  371. 可主动执行next,主动生成下一个有效正则作为匹配正则
  372. */
  373. matchAll, /**
  374. matchAll() => str.matchAll(regExp)
  375. 匹配失败时主动执行next生成下一个有效正则
  376. 可主动执行next,主动生成下一个有效正则作为匹配正则
  377. */
  378. replace, /**
  379. //replace(callback, change) => str.replace(regExp,callback);
  380. change为true时,如果替换后的值与替换前一样,则自动调用next生成下一个有效正则继续执行replace(callback, change)
  381. change为false时,只要生成的正则有效则只执行一次replace
  382. 可主动执行next,主动生成下一个有效正则作为匹配正则
  383. */
  384. search, /**
  385. search() => str.search(regExp)
  386. 匹配失败时主动执行next生成下一个有效正则
  387. 可主动执行next,主动生成下一个有效正则作为匹配正则
  388. */
  389. split /**
  390. split(limit, change) => str.split(regExp,limit)
  391. change为true时,如果切割后数组长度为1,则自动调用next生成下一个有效正则继续执行split(limit, change)
  392. change为false时,只要生成的正则有效则只执行一次split
  393. 可主动执行next,主动生成下一个有效正则作为匹配正则
  394. */
  395. }
  396. };
  397. //示例
  398. const obj = exRegExp({
  399. reg: /ersd[[reg:(ka)+]].*ef(fa){[[reg]]}.*sas/g,
  400. fns: {
  401. reg([$0, $1]) {
  402. const range0 = range($0.length / $1.length, 1, true);
  403. range0.reg = range0.map(it => new Array(it).fill($1).join(''));
  404. return range0
  405. }
  406. },
  407. str: 'bvgvgersdkakakaeffafaeffafafasaskuyfu'
  408. });
  409. console.log('match:', obj.match()); //match1: ['ersdkakakaeffafaeffafafasas', 'fa', index: 5, input: 'bvgvgersdkakakaeffafaeffafafasaskuyfu', groups: undefined]
  410. obj.regExp = /[[reg:(ka)+]]ef(fa){[[reg]]}/g;
  411. console.log('match2:', obj.match()); //match2: ['kakaeffafa']