正则表达式惰性匹配
Regex lazy matching
我有这个字符串
(Mozilla/5.0 \(X11; Linux x86_64\) AppleWebKit/537.36 \(KHTML, like Gecko\) Chrome/data Safari/data2) /Producer (Skia/PDF m80) /CreationDate (D:20200420090009+00'00') /ModDate (D:20200420090009+00'00')
我想在 ( 或 ) 之前没有任何 \ 的情况下获得 () 的第一次出现。那样的话我会得到
(Mozilla/5.0 \(X11; Linux x86_64\) AppleWebKit/537.36 \(KHTML, like Gecko\) Chrome/data Safari/data2)
我正在使用这个正则表达式
\([\s\S]*[^\]{1}\)?
但是我得到了整个字符串
你的正则表达式可以这样分解。
[空格和换行符是为了清楚起见]
\( match a literal (
[\s\S]* match 0 or more of whitespace or not-whitespace (anything)
[^\]{1} match 1 thing which is not \
\)? optionally match a literal )
就是那个 [\s\S]*
什么都吃完了。
最后的?
并不意味着懒惰,它使匹配)
成为可选的。为了懒惰,?
必须放在开放式限定符前面,例如 *?
或 +?
或 {3,}?
或 {1,5}?
.
为了只匹配第一组括号,我们想要延迟匹配未转义括号之间的任何内容。惰性匹配任何东西都很容易 .*?
.
匹配未转义的括号有点难。我们可以匹配 [^\]\)
,但这需要一个字符来匹配。如果左括号位于字符串的开头,这将不起作用,因为 (
之前没有字符。我们也可以通过匹配字符串的开头来解决这个问题:(?:[^\]|^)\)
.
(?: non-capturing group
[^\] match a non \
| or
^ the beginning of the string
)
\( match a literal (
.*? lazy match 0 or more of anything
[^\] match a non \
\) match a literal )
但这将被 ()
挫败。 It will match all of ()(foo)
.
(?:[^\]|^)
匹配字符串的开头。 \(
匹配第一个 (
。剩下 .*?[^\]\)
看着 )(foo)
。第一个 )
不匹配,因为没有前导字符,它已经被消耗了。所以 .*?
吞噬字符直到他的 o)
匹配 [^\]\)
.
边界问题negative look behinds解决得比较好。 (?<!\)
表示前面的字符不能是 \
,它根本不包含任何字符。 Lookbehinds 不会消耗它们匹配的内容,因此它们可用于向后和向前窥视。大多数(但不是全部)正则表达式引擎都支持它们。
(?<!\) \( match a literal ( which is not after a \
.*? lazy match 0 or more of anything
(?<!\) \) match a literal ) which is not after a \
但是,有一些库可以解析用户代理。 ua-parser 有多种语言的库,
我有这个字符串
(Mozilla/5.0 \(X11; Linux x86_64\) AppleWebKit/537.36 \(KHTML, like Gecko\) Chrome/data Safari/data2) /Producer (Skia/PDF m80) /CreationDate (D:20200420090009+00'00') /ModDate (D:20200420090009+00'00')
我想在 ( 或 ) 之前没有任何 \ 的情况下获得 () 的第一次出现。那样的话我会得到
(Mozilla/5.0 \(X11; Linux x86_64\) AppleWebKit/537.36 \(KHTML, like Gecko\) Chrome/data Safari/data2)
我正在使用这个正则表达式
\([\s\S]*[^\]{1}\)?
但是我得到了整个字符串
你的正则表达式可以这样分解。
[空格和换行符是为了清楚起见]
\( match a literal (
[\s\S]* match 0 or more of whitespace or not-whitespace (anything)
[^\]{1} match 1 thing which is not \
\)? optionally match a literal )
就是那个 [\s\S]*
什么都吃完了。
最后的?
并不意味着懒惰,它使匹配)
成为可选的。为了懒惰,?
必须放在开放式限定符前面,例如 *?
或 +?
或 {3,}?
或 {1,5}?
.
为了只匹配第一组括号,我们想要延迟匹配未转义括号之间的任何内容。惰性匹配任何东西都很容易 .*?
.
匹配未转义的括号有点难。我们可以匹配 [^\]\)
,但这需要一个字符来匹配。如果左括号位于字符串的开头,这将不起作用,因为 (
之前没有字符。我们也可以通过匹配字符串的开头来解决这个问题:(?:[^\]|^)\)
.
(?: non-capturing group
[^\] match a non \
| or
^ the beginning of the string
)
\( match a literal (
.*? lazy match 0 or more of anything
[^\] match a non \
\) match a literal )
但这将被 ()
挫败。 It will match all of ()(foo)
.
(?:[^\]|^)
匹配字符串的开头。 \(
匹配第一个 (
。剩下 .*?[^\]\)
看着 )(foo)
。第一个 )
不匹配,因为没有前导字符,它已经被消耗了。所以 .*?
吞噬字符直到他的 o)
匹配 [^\]\)
.
边界问题negative look behinds解决得比较好。 (?<!\)
表示前面的字符不能是 \
,它根本不包含任何字符。 Lookbehinds 不会消耗它们匹配的内容,因此它们可用于向后和向前窥视。大多数(但不是全部)正则表达式引擎都支持它们。
(?<!\) \( match a literal ( which is not after a \
.*? lazy match 0 or more of anything
(?<!\) \) match a literal ) which is not after a \
但是,有一些库可以解析用户代理。 ua-parser 有多种语言的库,