正则表达式:
(?|`(?>[^`\\]|\\.|``)*`|'(?>[^'\\]|\\.|'')*'|"(?>[^"\\]|\\.|"")*"|(\?{1,2})|(:{1,2})([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))
示例输入:
INSERT INTO xyz WHERE
a=?
and b="what?"
and ??="cheese"
and `col?`='OK'
and ::col='another'
and last!=:least
https://regex101.com/r/HnTVXx/6
它应与?
,??
,:xyz
和::xyz
匹配,但如果它们位于反引号字符串,双引号字符串或单引号内,则不匹配字符串。
当我尝试使用非常大的输入在PHP中运行时,我从PREG_RECURSION_LIMIT_ERROR
获得preg_last_error()
。
如何简化此正则表达式模式,以便它不会进行如此多的递归?
以下是使用Niet's optimized regex在PHP中显示错误的一些测试代码:https://3v4l.org/GdtmP错误代码6为PREG_JIT_STACKLIMIT_ERROR
。我见过的另一个是3 = PREG_RECURSION_LIMIT_ERROR
答案 0 :(得分:3)
使用这种模式可以实现“匹配这个东西,但不是在这种情况下”的一般概念:
(don't match this(*SKIP)(*FAIL)|match this)
在你的情况下,你需要像...这样的东西。
(
(['"`]) # capture this quote character
(?:\\.|(?!\1).)*+ # any escaped character, or
# any character that isn't the captured one
\1 # the captured quote again
(*SKIP)(*FAIL) # ignore this
|
\?\?? # one or two question marks
|
::?\w+ # word characters marked with one or two colons
)x
答案 1 :(得分:1)
同样的想法是跳过引用的部分((*SKIP)(*F)
组合),还有两种技术来减少正则表达式引擎的工作:
这两种技术有一些共同之处:限制交替的成本。
当您的模式以交替开始时,第一个字符区分很有用。一开始交替的问题是应该测试每个分支以便识别模式失败的位置。由于大多数时候,字符串中存在许多失败的位置,因此迅速丢弃它们构成了显着的改进。
例如,像"...|'...|`...|:...
这样的东西也可以这样写:
(?=["'`:])(?:"...|'...|`...|:...)
或
["'`:](?:(?<=")...|(?<=')...|(?<=`)...|(?<=:)...)
这样,每个不以这些字符["'`:]
之一开头的位置会立即被第一个标记拒绝,而不会测试每个分支。
展开的模式包括将以下内容重写为:" (?:[^"\\]|\\.)* "
:
" [^"\\]* (?: \\. [^"\\]* )* "
请注意,此设计消除了更改并大幅减少了步骤数:
basic
unrolled
使用这两种技术,您的模式可以这样写:
~
[`'"?:]
(?:
(?<=`) [^`\\]*+ (?s:\\.[^`\\]*|``[^`\\]*)*+ ` (*SKIP) (*F)
|
(?<=') [^'\\]*+ (?s:\\.[^'\\]*|''[^'\\]*)*+ ' (*SKIP) (*F)
|
(?<=") [^"\\]*+ (?s:\\.[^"\\]*|""[^"\\]*)*+ " (*SKIP) (*F)
|
(?<=\?) \??
|
(?<=:) :? ([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)
)
~x
其他方式:您可以构建一个匹配所有字符串且连续结果的模式,而不是在开头使用替换(改进或不改进)。一般设计是:
\G (all I don't want) (*SKIP) \K (what I am looking for)
\G
是一个锚,它匹配前一个结果之后的位置或字符串的开头。使用它启动模式可确保所有匹配都是连续的。在这种情况下(在模式的开头和整个模式的因子中),您也可以用A修饰符替换它。
这给出了:
~
[^`'"?:]*
(?:
` [^`\\]*+ (?s:\\.[^`\\]*|``[^`\\]*)*+ ` [^`'"?:]*
|
' [^'\\]*+ (?s:\\.[^'\\]*|''[^'\\]*)*+ ' [^`'"?:]*
|
" [^"\\]*+ (?s:\\.[^"\\]*|""[^"\\]*)*+ " [^`'"?:]*
)*
\K # only the part of the match after this position is returned
(*SKIP) # if the next subpattern fails, the contiguity is broken at this position
(?:
\?{1,2}
|
:{1,2} ([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)
)
~Ax