使用 Regex 匹配函数体
Match the body of a function using Regex
给定一个虚拟函数:
public function handle()
{
if (isset($input['data']) {
switch($data) {
...
}
} else {
switch($data) {
...
}
}
}
我的目的是获取该函数的内容,问题是匹配花括号的嵌套模式 {...}
。
我遇到了 recursive patterns 但无法理解与函数主体匹配的正则表达式。
我尝试了以下方法(无递归):
$pattern = "/function\shandle\([a-zA-Z0-9_$\s,]+\)?". // match "function handle(...)"
'[\n\s]?[\t\s]*'. // regardless of the indentation preceding the {
'{([^{}]*)}/'; // find everything within braces.
preg_match($pattern, $contents, $match);
那个模式根本不匹配。我确定这是最后一点错误 '{([^{}]*)}/'
因为当体内没有其他括号时该模式有效。
将其替换为:
'{([^}]*)}/';
一直匹配到if
语句中switch的closing }
就停止了(包括switch的}
,不包括if
的switch) .
与此模式一样,结果相同:
'{(\K[^}]*(?=)})/m';
更新 #2
根据别人的评论
^\s*[\w\s]+\(.*\)\s*\K({((?>"(?:[^"\]*+|\.)*"|'(?:[^'\]*+|\.)*'|//.*$|/\*[\s\S]*?\*/|#.*$|<<<\s*["']?(\w+)["']?[^;]+;$|[^{}<'"/#]++|[^{}]++|(?1))*)})
注意:如果您知道您的输入不包含 PHP 语法中的 {
或 }
,那么一个简短的 RegEx 即 {((?>[^{}]++|(?R))*)}
就足够了.
这么长的正则表达式,在什么邪恶情况下有效?
- 您在引号
["']
之间的字符串中有 [{}]
- 你把那些引号互相转义了
- 您有
[{}]
个评论区。 //...
或 /*...*/
或 #...
- 您在 heredoc 或 nowdoc
<<<STR
或 <<<['"]STR['"]
中有 [{}]
否则就意味着有一对 opening/closing 大括号,嵌套大括号的深度并不重要。
我们有失败的案例吗?
不,除非你的代码中有一个火星人。
^ \s* [\w\s]+ \( .* \) \s* \K # how it matches a function definition
( # (1 start)
{ # opening brace
( # (2 start)
(?> # atomic grouping (for its non-capturing purpose only)
"(?: [^"\]*+ | \ . )*" # double quoted strings
| '(?: [^'\]*+ | \ . )*' # single quoted strings
| // .* $ # a comment block starting with //
| /\* [\s\S]*? \*/ # a multi line comment block /*...*/
| \# .* $ # a single line comment block starting with #...
| <<< \s* ["']? # heredocs and nowdocs
( \w+ ) # (3) ^
["']? [^;]+ ; $ # ^
| [^{}<'"/#]++ # force engine to backtack if it encounters special characters [<'"/#] (possessive)
| [^{}]++ # default matching bahaviour (possessive)
| (?1) # recurse 1st capturing group
)* # zero to many times of atomic group
) # (2 end)
} # closing brace
) # (1 end)
格式化由@sln的RegexFormatter软件完成。
我在现场演示中提供了什么?
Laravel 的 Eloquent Model.php 文件(~3500 行)随机作为输入。看看这个:
Live demo
这可以从内联函数块 (.c) 中输出头文件 (.h)
查找正则表达式:
(void\s[^{};]*)\n^\{($[^}$]*)\}$
替换为:
;
输入:
void bar(int var)
{
foo(var);
foo2();
}
将输出:
void bar(int var);
获取具有第二个匹配模式的功能块主体:
将输出:
foo(var);
foo2();
给定一个虚拟函数:
public function handle()
{
if (isset($input['data']) {
switch($data) {
...
}
} else {
switch($data) {
...
}
}
}
我的目的是获取该函数的内容,问题是匹配花括号的嵌套模式 {...}
。
我遇到了 recursive patterns 但无法理解与函数主体匹配的正则表达式。
我尝试了以下方法(无递归):
$pattern = "/function\shandle\([a-zA-Z0-9_$\s,]+\)?". // match "function handle(...)"
'[\n\s]?[\t\s]*'. // regardless of the indentation preceding the {
'{([^{}]*)}/'; // find everything within braces.
preg_match($pattern, $contents, $match);
那个模式根本不匹配。我确定这是最后一点错误 '{([^{}]*)}/'
因为当体内没有其他括号时该模式有效。
将其替换为:
'{([^}]*)}/';
一直匹配到if
语句中switch的closing }
就停止了(包括switch的}
,不包括if
的switch) .
与此模式一样,结果相同:
'{(\K[^}]*(?=)})/m';
更新 #2
根据别人的评论
^\s*[\w\s]+\(.*\)\s*\K({((?>"(?:[^"\]*+|\.)*"|'(?:[^'\]*+|\.)*'|//.*$|/\*[\s\S]*?\*/|#.*$|<<<\s*["']?(\w+)["']?[^;]+;$|[^{}<'"/#]++|[^{}]++|(?1))*)})
注意:如果您知道您的输入不包含 PHP 语法中的 {
或 }
,那么一个简短的 RegEx 即 {((?>[^{}]++|(?R))*)}
就足够了.
这么长的正则表达式,在什么邪恶情况下有效?
- 您在引号
["']
之间的字符串中有 - 你把那些引号互相转义了
- 您有
[{}]
个评论区。//...
或/*...*/
或#...
- 您在 heredoc 或 nowdoc
<<<STR
或<<<['"]STR['"]
中有
[{}]
[{}]
否则就意味着有一对 opening/closing 大括号,嵌套大括号的深度并不重要。
我们有失败的案例吗?
不,除非你的代码中有一个火星人。
^ \s* [\w\s]+ \( .* \) \s* \K # how it matches a function definition
( # (1 start)
{ # opening brace
( # (2 start)
(?> # atomic grouping (for its non-capturing purpose only)
"(?: [^"\]*+ | \ . )*" # double quoted strings
| '(?: [^'\]*+ | \ . )*' # single quoted strings
| // .* $ # a comment block starting with //
| /\* [\s\S]*? \*/ # a multi line comment block /*...*/
| \# .* $ # a single line comment block starting with #...
| <<< \s* ["']? # heredocs and nowdocs
( \w+ ) # (3) ^
["']? [^;]+ ; $ # ^
| [^{}<'"/#]++ # force engine to backtack if it encounters special characters [<'"/#] (possessive)
| [^{}]++ # default matching bahaviour (possessive)
| (?1) # recurse 1st capturing group
)* # zero to many times of atomic group
) # (2 end)
} # closing brace
) # (1 end)
格式化由@sln的RegexFormatter软件完成。
我在现场演示中提供了什么?
Laravel 的 Eloquent Model.php 文件(~3500 行)随机作为输入。看看这个: Live demo
这可以从内联函数块 (.c) 中输出头文件 (.h)
查找正则表达式:
(void\s[^{};]*)\n^\{($[^}$]*)\}$
替换为:
;
输入:
void bar(int var)
{
foo(var);
foo2();
}
将输出:
void bar(int var);
获取具有第二个匹配模式的功能块主体:
将输出:
foo(var);
foo2();