正则求助:找出非闭合的img标签

@Ta 2022-02-10发布,2022-02-10修改 17098点击

测试文本


<img 
v-if="test>a" 
src="url">
<text>文本</text>

如何找出上文中未闭合的img标签呢:

<img 
v-if="test>a" 
src="url">

其实就是如何排除带 <> 的属性干扰

手动@老虎会游泳
小米MIX2s(白)

回复列表(37|隐藏机器人聊天)
  • @Ta / 2022-02-10 / /

    @水木易安

    /<img\b\s*([^\s<='"\/>]+(=([^"'<\/>]+|"[^"]*"|'[^']*'))?\s*)*>/
    

    可以匹配以下内容:

    <img>
    
    <img checked>
    
    <img selected x-aaa="b>c/d" data-bbb='e<f/g' height=500.25>
    

    不匹配以下内容:

    <img/>
    
    <img checked />
    

    匹配以下内容时遇到回溯灾难(匹配永不结束),需要优化:

    <img selected x-aaa="b>c/d" data-bbb='e<f/g' height=500.25 />
    

    关于回溯灾难:https://hu60.cn/q.php/bbs.topic.89212.html

    使用 https://regex101.com/ 进行正则表达式调试。注意:只有PCRE可以使用调试器,如果选了JS,调试器选项会消失。

  • @Ta / 2022-02-10 / /
    /<img\b\s*([A-Za-z_][A-Za-z0-9-_]*(=(-?[0-9]+(\.[0-9]+)?([Ee][+-]?[0-9]+)?|'[^']*'|"[^"]*"))?\s*)*>/
    

    写成这样在js里能够不匹配,但依然用时很久。在PHP里还是灾难性回溯。

    image.png

  • @Ta / 2022-02-10 / /

    把规则放简单一点,忽略height=500.25这种不加引号的写法,和selected这种没有值的写法,就不会灾难性回溯了:

    /<img\s*([A-Za-z_][A-Za-z0-9-_]*=('[^']*'|"[^"]*")\s*)+>/
    

    image.png

    image.png

  • @Ta / 2022-02-10 / /

    至于“如何排除带 <> 的属性干扰”,答案倒是很简单:

    ('[^']*'|"[^"]*")
    
  • @Ta / 2022-02-10 / /

    看起来这种写法的开销很大,这个?是性能杀手。

    (=('[^']*'|"[^"]*"))?
    

    这个表达式在灾难性回溯的边缘,内容稍微多一点就要匹配很久:

    /<img\s*([A-Za-z_][A-Za-z0-9-_]*(=('[^']*'|"[^"]*"))?\s*)+>/
    

    image.png

    image.png

    匹配尝试了11万步:

    image.png

  • @Ta / 2022-02-10 / /

    总算找到灾难性回溯的根源了。
    其实结尾的\s*才是性能杀手。\s*表示:属性之间可以有空格,也可以没有空格。
    而“属性之间没有空格”这种选择,才是导致一系列回溯的原因,因为正则表达式引擎在试图把单个属性拆分成各个不同的部分(比如selected可以切分成selected,还可以切分成selected,等等)。

    改成\s+并且放在属性的前面,就不会有这种问题了。

    /<img(\s+[A-Za-z_][A-Za-z0-9-_]*(=(-?[0-9]+(\.[0-9]+)?([Ee][+-]?[0-9]+)?|'[^']*'|"[^"]*"))?)*\s*>/
    

    现在最难的不匹配只需300步:

    image.png

  • @Ta / 2022-02-10 / /

    1楼的否定集合表示法也可以这样改造:

    /<img(\s+[^\s<='"\/>]+(=([^"'<\/>]+|"[^"]*"|'[^']*'))?)*\s*>/
    

    匹配48步:

    image.png

    不匹配300步:

    image.png

    性能相当优秀。

  • @Ta / 2022-02-10 / /

    上面的表达式依然不完备,比如这就无法匹配到,而它是合法的HTML代码:

    image.png

    HTML代码

    浏览器解析后:

    图片.png

  • @Ta / 2022-02-10 / /

    看起来,正则表达式很难完美的完成匹配,至少手写的不太行。总会有各种各样未能匹配到的特例。

    就连完美的匹配这个字符串都很难:

    "abc\"def\\" + "\\ghi\njkl"
    

    不过如果只是匹配非常标准的HTML写法,没有不打空格不打引号等特例,那('[^']*'|"[^"]*")应该就足够了。

  • @Ta / 2022-02-10 / /

    举一个错误匹配的绝佳例子:

    <div data-test='<img src="xxx.jpg">'></div>
    

    在不进行全面语法分析的情况下,没有任何办法避免错误匹配到这个<img src="xxx.jpg">

  • @Ta / 2022-02-10 / /

    好家伙,
    不愧是老虎,
    当我琢磨了半天的正则无果之后,
    扔了一个求助帖就睡了,
    然后第二天被长篇大论的回复震惊到让我联想到这个视频

    视频链接

    非常感谢,我先琢磨一下回复。

    小米MIX2s(白)

  • @Ta / 2022-02-10 / /

    @老虎会游泳,请问一下#6 楼中的 RegEx Debugger 是什么来的呀
    小米MIX2s(白)

  • @Ta / 2022-02-10 / /

    找到了 登录账号以后就能用了
    小米MIX2s(白)

  • @Ta / 2022-02-10 / /

    @水木易安,不需要登录,只是只有PCRE可以使用调试器,如果选了JS,调试器选项会消失。此外,还要先输入一个表达式才能点调试器。

  • @Ta / 2022-02-10 / /

    @老虎会游泳,原来如此
    小米MIX2s(白)

  • @Ta / 2022-02-10 / /

    除了@老虎会游泳 提到的regex101.com,再安利一个exe的正则神器regexbuddy

    坏孩子,其实你很好,但是还不够好

  • @Ta / 2022-02-10 / /

    regex101.com好像还有人给做了个中文版的: 嗨正则https://hiregex.com(这个好像还不是反代 ?)
    坏孩子,其实你很好,但是还不够好

  • @Ta / 2022-03-08 / /

    @老虎会游泳,新的问题

    第一个是箭头函数,好像误判了

    /<img(\s+[^\s<='"\/>]+(=([^"'<\/>]+|"[^"]*"|'[^']*'))?)*\s*>(.*<\/img>)?/gmi

    case:

    
    
    		<img onclick={()=>alert({msg:123})} src="../../image/home/right_icon.png" style="height:9px" />
    
    
    		<img>
    		<img checked>
    
    		<img />
    
    		<img v-if="a>b"></img>
    
    

    小米MIX2s(白)

  • @Ta / 2022-03-08 / /
添加新回复
回复需要登录