前提原则

  1. 表达式内固定内容的字符串能不写尽量不写,能简写的尽量简化来写(如前面写的abc部分不能写死)
  2. 可以少量使用前瞻(正向),后瞻(反向)基本不学,学了还要研究那些浏览器支持,不敢用,太多了也学不动。
  3. NoJS(Not Only JavaScript);不仅仅是浏览器中的js;不过js的正则/exp/字面量写法简洁到没盆友(哪个语言),函数、对象、字符串统统不需要;不接受反驳。

假设待匹配的文本

htmlRaw=`
<div ***="***
    ***" class="***" ***="***">
    class=" matchX 1"
    <div ***="***
        ***" class="*** matchX ***" ***="*** excludeX ***">
        class=" matchX 2"
    </div>
    class=" matchX 3"
</div>

<div ***="***
    ***" class="***" ***="***">
    class=" matchX 4"
    <div ***="***
        ***" class="*** ***" ***="***">
        class=" matchX 5"
    </div>
    class=" matchX 6"
</div>

......
`;
//注: *** 不是固定内容,但不会出现未转义的xml实体、matchX、excludeX
//注: class=" matchX 123456"纯文本只做演示干扰之用,不应当作为特征

//如果数据对换行不敏感,应优先转换成没有换行的,大概率可以简化正则逻辑
htmlNoWrap=htmlRaw.replace(/[\r\n]+/g," ");

//正则测试代码
(/[\s\S]*/.exec(htmlRaw)||[])[0]

不要单个字符的匹配

可以直接使用 [^>]matchX限定在<> HTML标记内,意思就是<>中的文本不要出现结尾的>字符。

单个字符还算简单:

//有效
/<div[^>]+matchX[^>]+>/.exec(htmlRaw)[0]

如果不限定在<>标记内,可能会匹配出界;并且这种不限定,迟早会出乱子:

//无效
/<div.+?matchX.+?>/.exec(htmlNoWrap)[0]

/<div[\s\S]+?matchX[\s\S]+?>/.exec(htmlRaw)[0] //有换行符就是又长又难看

其他单个字符场景另行灵活运用。

不要一个字符串的匹配

[^]语法只能排除掉单个字符,不要一个字符串咋办?硬是要写成[^abc],会把a、b、c字符全部排除掉;除了使用前瞻,好像还没有别的简单办法。

使用本文开头的(.(?!排除的字符串))+结构就能达到目的,核心就在(?:[^>](?!excludeX))*

//有效
/<div[^>]*>[^<]+<div(?:[^>](?!excludeX))*>[^<]+<\/div>[^<]+<\/div>/.exec(htmlRaw)[0]

要包含一个字符串的匹配,直接写需要的字符串即可,相对简单太多,就不写这种例子了。

未研究(.(?!排除的字符串))+结构的性能。

对于.+(?!排除)不能工作的释疑

由于(?!排除)并不会作用于贪婪匹配到的每一个字符串,只会作用于.+贪婪匹配到的最后一个字符;意思就是前瞻不能阻止+对最后一个字符之前的所有字符进行贪婪匹配。

额外记录

/(\d+)(?!\.1)/.exec("123.141") 目测是这样的:
> 123:\d+贪婪匹配到.为止
> 12:发现 123.1 不符合(?!\.1),后退一位
> 没有表达式了,返回 12

/(.+)(?!\.1)/.exec("123.141") 目测是这样的:
> 123.141:.+贪婪匹配到结尾
> 123.141 : 符合(?!\.1)
> 没有表达式了,返回 123.141

/(.(?!\.1))+/.exec("123.141") 目测是这样的:
> 1:.匹配到新的一位
> 1:123 符合(?!\.1)
> 12:.匹配到新的一位
> 12:123.符合(?!\.1)
> 123:.匹配到新的一位
> 12:发现 123.1 不符合(?!\.1),后退一位,并退出循环
> 没有表达式了,返回 12

如果要对每个字符进行前瞻检查,唯有最后一种写法比较好理解。

04-08 14:23