前言
有时候需要实现对js源文件中的url字符串做拦截预处理,或者前端js语法高亮,或者需要对动态加载的关键源码做混淆保护,在某些步骤实现之前,有一个步骤是需要提炼出所有的合法字符串。
目标:检测源文件文本中的字符串,合法的双(单)引号之间的内容与引号自身。
实现方式:正则表达式匹配。
改进:优化正则表达式,加快匹配速度与尽可能减少匹配时占用的内存空间。
基本存在情况:“”,’’,”\””,’\’’,所有的合法字符情况都是以上四种情况的拓展
分解规则:双引号之间存在双引号,前面必须是转义符 “\”,单引号同理。所以匹配到后面一个引号时,必须检测前面是是否存在连续奇数长度的转义符”\”。
比如:
”\””,”\\\””,”\\\\\”” 正确匹配目标是3个”\””,”\\\””,”\\\\\””。
a = "\\";"test".split(""); 正确匹配目标是3个:”\\”, “test”,””, 而不是2个"\\";",".split(";。
但是js中暂时不支持正向反查的形式(?<!),需要转换下思路:
根据正则从左到右的顺序,优先判断一组转义\”或者\’,,使用 /” (\\.|.)+?”/g
例如:”\\\””;all
1:匹配起始” --> “
2:第一位\与第二位\不能组成\”,符合任意字符匹配”.”,下一步 --> “\
3:第二位\与第三位\不能组成\”,符合任意字符匹配”.”,继续下一步 --> ”\\
4:第三位\与第四位” 可以组成\”, 继续下一步 --> ”\\\”
5:第五位是”, 匹配结束” --> 得到目标字符串 --> ”\\\””
第一次正则表达式
/""|"(\\"|.)+?"|''|'(\\'|.)+?'/gm
测试:
'aa,\"\'\",,aa,,,bbbb,,,,\\\',,cc,dd,';test, ==> 'aa,\"\'\",,aa,,,bbbb,,,,\\\',,cc,dd,' 1
"" ==>""
'' ==>''
"\\" ==>"\\"
"\'\\\"" ==>"\'\\\""
"\"\"" ==> "\"\""
达到效果预期。
优化正则表达
原始版: /""|"(\\"|.)+?"|''|'(\\'|.)+?'/gm
优化第1版:/\"(\\"|.)*?\"|\'(\\'|.)*?\'/gm
优化第2版:/("|')(\\.|.)*?\1/gm 或者 /(["'])(\\["']|.)*?\1/gm 或者 /("|')(?:\\.|.)*?\1/gm
其中2是1的字面简化写法,但是对比1与2,2多了一个\1,正则表达式需要缓存开始位("|')用于结束位匹配,放弃2。
那么1是否可以再优化?
可以:使用非捕获型,减少捕获缓存
最终效果:
/\"(?:\\"|.)*?\"|\'(?:\\'|.)*?\'/gm
后记
测试中只有达到400万长度的字符串才会有明显的性能差,400万长度在chrome浏览器中是【2:380ms】【1(终版):180ms】,一般来说,正常使用的文本不会这么长到变态,所以最终优化版与前面几个版本之间的性能在正常情况下的差距几乎可以忽略不及,最终优化版只是一种自嗨的高潮而已。因为这样很爽啊。
推荐一个老外写的js正则表达式可视化,: https://github.com/JexCheng/regulex
可视化的正则表达式真好看,end!
Bruce-CZ原创
----看着流莺的羽毛一点点暗淡下去,他觉得好像什么东西死去一样,堵住喉咙一样难受,是什么呢,他又说不出。