正则表达式:后面不要包含指定的字符串内容
以前只会
/<abc(?!def).+>/.exec("<abcdef>\n<abczzz>")
,匹配到<abczzz>
,这种简单的固定写法,但实际使用次数几乎趋近于0。很多场景不能写死
abc
,顺理成章的就写成了/<.+(?!def).+>/.exec("<abcdef>\n<abczzz>")
,咦?咋把前面的匹配到了!从入门到放弃。昨天(2019-04-07)随手写了一下
/<(?:.(?!def))+>/.exec("<abcdef>\n<abczzz>")
,原来是对.+(?!排除的字符串)
这个结构能起到的作用理解错了,怪不得达不到预期,(.(?!排除的字符串))+
才是正解。留下一个未解的问题,每个字符后面排除一下的能良好工作,一堆未定长度字符后排除一下怎么就不能工作,前瞻不会和前面的
+、*、{}
起作用吗?解释看结尾。
附:
/<(?!.+def).+>/.exec("<abcdefzzz>\n<abczzz>")
写法也可以。可能是结尾的.+导致的不能匹配,但这样写还是不行:/<.+(?!def)zzz>/.exec("<abcdefzzz>\n<abczzz>")
。
正则表达式匹配指定内容后面要或不要包含指定的字符串内容:
- 要:比较简单,写上这个要的即可
- 不要:比要复杂很多,如何排除掉?
前提原则
- 表达式内固定内容的字符串能不写尽量不写,能简写的尽量简化来写(如前面写的
abc
部分不能写死) - 可以少量使用前瞻(正向),后瞻(反向)基本不学,学了还要研究那些浏览器支持,不敢用,太多了也学不动。
- 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]
不要单个字符的匹配
匹配出
div.matchX
标签:<div ***="*** ***" class="*** matchX ***" ***="***">
可以直接使用 [^>]
把matchX
限定在<>
HTML标记内,意思就是<>
中的文本不要出现结尾的>
字符。
单个字符还算简单:
//有效
/<div[^>]+matchX[^>]+>/.exec(htmlRaw)[0]
如果不限定在<>
标记内,可能会匹配出界;并且这种不限定,迟早会出乱子:
//无效
/<div.+?matchX.+?>/.exec(htmlNoWrap)[0]
/<div[\s\S]+?matchX[\s\S]+?>/.exec(htmlRaw)[0] //有换行符就是又长又难看
其他单个字符场景另行灵活运用。
不要一个字符串的匹配
匹配出第一层不带
excludeX
文本内容的第一块div
,就是返回包含matchX 4
的那块div
[^]
语法只能排除掉单个字符,不要一个字符串咋办?硬是要写成[^abc]
,会把a、b、c
字符全部排除掉;除了使用前瞻,好像还没有别的简单办法。
使用本文开头的(.(?!排除的字符串))+
结构就能达到目的,核心就在(?:[^>](?!excludeX))*
:
//有效
/<div[^>]*>[^<]+<div(?:[^>](?!excludeX))*>[^<]+<\/div>[^<]+<\/div>/.exec(htmlRaw)[0]
要包含一个字符串的匹配,直接写需要的字符串即可,相对简单太多,就不写这种例子了。
未研究(.(?!排除的字符串))+
结构的性能。
对于.+(?!排除)
不能工作的释疑
由于(?!排除)
并不会作用于贪婪匹配到的每一个字符串,只会作用于.+
贪婪匹配到的最后一个字符;意思就是前瞻不能阻止+
对最后一个字符之前的所有字符进行贪婪匹配。
/<.+(?!def).+>/.exec("<abcdef>\n<abczzz>")
第一个 .+ 匹配到了 abcde,之后是 f,不是 def,第二个 .+ 匹配 f,符合正则
额外记录
/(\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
如果要对每个字符进行前瞻检查,唯有最后一种写法比较好理解。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述