PHP preg_match_all 模式中的子模式名称
PHP preg_match_all subpattern names in a pattern
任务很明确。在输入中我们有一个可变的正则表达式模式,它应该包含命名的子模式,在输出中我们需要得到一个子模式名称数组:
function get_subpattern_names($any_input_pattern) {
// What pattern to use here?
$pattern_to_get_names = '/.../';
preg_match_all($pattern_to_get_names, $any_input_pattern, $matches);
return $matches;
}
所以问题是在上面的函数中使用什么作为$pattern_to_get_names
?
例如:
get_subpattern_names('/(?P<name>\w+): (?P<digit>\d+)/');
应该return:
array('name', 'digit');
P.S.: 根据 PCRE documentation 子模式名称最多由 32 个字母数字字符和下划线组成。
由于我们无法控制输入模式,因此我们需要考虑所有可能的命名语法。根据 PHP documentation 他们是:
(?P<name>pattern)
、(?<name>pattern)
和 (?'name'pattern)
。
我们还需要考虑嵌套子模式,例如:
(?<name1>.*(?<name2>pattern).*)
.
无需计算重复名称、保留外观顺序或获取数字、非捕获或其他类型的子模式。仅列出名称(如果存在)。
您可以使用
获得所有有效命名捕获组名称的列表
"~(?<!\\)(?:\\{2})*\(\?(?|P?<([_A-Za-z]\w{0,31})>|'([_A-Za-z]\w{0,31})')~"
参见regex and an online PHP demo。
要点是匹配未转义的 (
后跟 ?
后跟 P<
或 <
然后有一个组名以 >
或 '
结尾的模式后跟组名模式,然后是 '
.
$rx = "~(?<!\\)(?:\\{2})*\(\?(?|P?<([_A-Za-z]\w{0,31})>|'([_A-Za-z]\w{0,31})')~";
$s = "(?P<name>\w+): (?<name2>\w+): (?'digit'\d+)";
preg_match_all($rx, $s, $res);
print_r($res[1]);
产量
Array
(
[0] => name
[1] => name2
[2] => digit
)
图案详情
(?<!\)
- 没有\
紧靠当前位置 的左侧
(?:\\)*
- 0+ 双反斜杠(允许 (
之前的任何转义反斜杠)
\(
- (
\?
- 一个?
(?|P?<([_A-Za-z]\w{0,31})>|'([_A-Za-z]\w{0,31})')
- 分支重置组:
P?<([_A-Za-z]\w{0,31})>
- 一个可选的 P
、<
、一个 _
或一个 ASCII 字母,0 到 31 个字符 (digits/letters/_
)(捕获到第 1 组)和 >
|
- 或
'([_A-Za-z]\w{0,31})'
- '
,一个 _
或一个 ASCII 字母,0 到 31 个字字符 (digits/letters/_
)(也捕获到第 1 组),然后 '
组名模式全部捕获到组1中,你只需要得到$res[1]
。
Wiktor 的解决方案看起来确实很彻底,但这是我想出的。
print_r(get_subpattern_names('/(?P<name>\w+): (?P<digit>\d+)/'));
function get_subpattern_names($input_pattern){
preg_match_all('/\?P\<(.+?)\>/i', $input_pattern, $matches);
return $matches[1];
}
这应该适用于大多数情况。更重要的是,这更具可读性和不言自明。
基本上,我会搜索 ?P<
,然后搜索 (.+?)
,这会转换为 angular 括号之间内容的 non-greedy 版本。然后函数只是 returns $matches
数组中的第一个偏移量,它指向匹配的第一组括号。
任务很明确。在输入中我们有一个可变的正则表达式模式,它应该包含命名的子模式,在输出中我们需要得到一个子模式名称数组:
function get_subpattern_names($any_input_pattern) {
// What pattern to use here?
$pattern_to_get_names = '/.../';
preg_match_all($pattern_to_get_names, $any_input_pattern, $matches);
return $matches;
}
所以问题是在上面的函数中使用什么作为$pattern_to_get_names
?
例如:
get_subpattern_names('/(?P<name>\w+): (?P<digit>\d+)/');
应该return:
array('name', 'digit');
P.S.: 根据 PCRE documentation 子模式名称最多由 32 个字母数字字符和下划线组成。
由于我们无法控制输入模式,因此我们需要考虑所有可能的命名语法。根据 PHP documentation 他们是:
(?P<name>pattern)
、(?<name>pattern)
和 (?'name'pattern)
。
我们还需要考虑嵌套子模式,例如:
(?<name1>.*(?<name2>pattern).*)
.
无需计算重复名称、保留外观顺序或获取数字、非捕获或其他类型的子模式。仅列出名称(如果存在)。
您可以使用
获得所有有效命名捕获组名称的列表"~(?<!\\)(?:\\{2})*\(\?(?|P?<([_A-Za-z]\w{0,31})>|'([_A-Za-z]\w{0,31})')~"
参见regex and an online PHP demo。
要点是匹配未转义的 (
后跟 ?
后跟 P<
或 <
然后有一个组名以 >
或 '
结尾的模式后跟组名模式,然后是 '
.
$rx = "~(?<!\\)(?:\\{2})*\(\?(?|P?<([_A-Za-z]\w{0,31})>|'([_A-Za-z]\w{0,31})')~";
$s = "(?P<name>\w+): (?<name2>\w+): (?'digit'\d+)";
preg_match_all($rx, $s, $res);
print_r($res[1]);
产量
Array
(
[0] => name
[1] => name2
[2] => digit
)
图案详情
(?<!\)
- 没有\
紧靠当前位置 的左侧
(?:\\)*
- 0+ 双反斜杠(允许(
之前的任何转义反斜杠)\(
-(
\?
- 一个?
(?|P?<([_A-Za-z]\w{0,31})>|'([_A-Za-z]\w{0,31})')
- 分支重置组:P?<([_A-Za-z]\w{0,31})>
- 一个可选的P
、<
、一个_
或一个 ASCII 字母,0 到 31 个字符 (digits/letters/_
)(捕获到第 1 组)和>
|
- 或'([_A-Za-z]\w{0,31})'
-'
,一个_
或一个 ASCII 字母,0 到 31 个字字符 (digits/letters/_
)(也捕获到第 1 组),然后'
组名模式全部捕获到组1中,你只需要得到$res[1]
。
Wiktor 的解决方案看起来确实很彻底,但这是我想出的。
print_r(get_subpattern_names('/(?P<name>\w+): (?P<digit>\d+)/'));
function get_subpattern_names($input_pattern){
preg_match_all('/\?P\<(.+?)\>/i', $input_pattern, $matches);
return $matches[1];
}
这应该适用于大多数情况。更重要的是,这更具可读性和不言自明。
基本上,我会搜索 ?P<
,然后搜索 (.+?)
,这会转换为 angular 括号之间内容的 non-greedy 版本。然后函数只是 returns $matches
数组中的第一个偏移量,它指向匹配的第一组括号。