正则表达式是平时开发中经常用到的技巧,大部分时候我们需要的是判断字符串有没有含有固定的模式串,但是某些场景下需要使用非获取匹配,下面根据一种常见的情景:
匹配所有含有 app://page.cb/myPage?id=xxxx
的地址,但是排除参数中携带downgrade=true
的地址。
具体用几个例子说明一下:
原始地址: app://page.cb/myPage
匹配结果: no,id不存在
原始地址: app://page.cb/myPage?id=123456
匹配结果: yes
原始地址: app://page.cb/myPage?param=123456
匹配结果: no, id不存在
原始地址: app://page.cb/myPage?id=123456&downgrade=true
匹配结果: no,downgrade为true
原始地址: app://page.cb/myPage?id=123456&downgrade=false
匹配结果: yes
原始地址: app://page.cb/myPage?downgrade=false&id=123456
匹配结果: yes
原始地址: app://page.cb/myPage?downgrade=true&id=123456
匹配结果: no,downgrade为true
匹配所有不含downgrade=true
的app://page.cb/myPage?id=xxxx
地址,这里可以使用正则表达式的非获取匹配,下面介绍非获取匹配的两种:
(?!pattern)
非获取匹配,正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。
(?<!pattern)
非获取匹配,反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。
根据非获取匹配的写法, 我们采用了正向否定查询,正则表达式如下:
app:\/\/page.cb\/myPage\?(((?!downgrade=true).)*id=\d+((?!downgrade=true).)*)
对其中的反向预查部分进行分割如下:
app:\/\/page.cb\/myPage\?(((?!downgrade=true).)*id=\d+((?!downgrade=true).)*)
可以在https://regex101.com 中尝试匹配。
分析以上正则表达式,即 id=\d+
的前后都不能含有 downgrade=true
,其中 ((?!downgrade=true).)*
从里到外看,(?!downgrade=true)
代表不含该字符串,然后.用于匹配id参数前后的其他参数,此处的.不能防止在 (?!downgrade=true)
的前面,会造成如果第一个参数是 downgrade=true
时被遗漏掉。
如果需要url中参数部分,其中$1即为参数部分。
测试case集合:
app://page.cb/myPage
app://page.cb/myPage?id=123456
app://page.cb/myPage?param=123456
app://page.cb/myPage?id=123456&downgrade=true
app://page.cb/myPage?id=123456&downgrade=false
app://page.cb/myPage?downgrade=false&id=123456
app://page.cb/myPage?downgrade=true&id=123456
由于各种语言对正则表达式支持程度不同,例如非获取匹配中的反向否定预查在javascript中不支持,但是在php、python、java中是支持的,正向否定预查在javascript、php、python、java中都支持,所以使用前一定要注意,同事需要考虑正则表达式的性能问题。