正则表达式:解析多个引用 name/value 对
regex: parsing multiple quoted name/value pairs
在 PHP 5.6/7.0 中工作。
我在几个正则表达式网站上尝试了几个问题和几个小时的几个正则表达式,但找不到任何东西可以满足我的需要。我有一个这样的字符串:
At vero eos et accusamus et iusto odio dignissimos ducimus
<!-- @@include default="/admin/creditapp/templates/longform/" try="/wTemplates/forms/templates/" file="credit_row_1.txt" -->
qui blanditiis praesentium voluptatum deleniti atque corrupti
<!-- @@include default="/admin/creditapp/templates/longform/" try="/wTemplates/forms/templates/" file="credit_row_2.txt" -->
quos dolores et quas excepturi sint
我正在从令牌中寻找以下匹配项:
<!-- @@include ...the whole thing... -->
default
/admin/creditapp/templates/longform
try
/wtemplates/forms/templates
file
credit_row_1.txt
每次找到整个组时自然重复。我可以循环文件并完成它,所以一次只需要一个实例就可以了。我能想出的唯一表达方式就是:
<!-- @@include (?:(try|default|file)=\"(.+?)\"?)(?:\s*)(?:(try|default|file)=\"(.+?)\"?)(?:\s*)(?:(try|default|file)=\"(.+?)\"?)(?:\s*)-->
这是巨大的,并且不允许其他可能性,比如,我不知道,“(try|foo|bar|default)”之类的,或者省略任何一个 "try" 或 "default," 例如“(foo|bar|文件)。”
在模板中
<!-- @@include -->
不变。里面可以是 2 到 n name=value 对。我试过了:
(<!-- @@include (?:(try|default|file)=\"(.+?)\" ?){1,3}-->)
但它只 returns 找到了姓氏=值。我想认为我很接近,但我无法解决。
PCRE 无法存储重复捕获组的不同内容。当组重复时,之前的内容被当前内容覆盖等等。
一种解决方法是使用 preg_match_all
并使用 \G
锚点匹配上一个匹配项之后的下一个位置(默认情况下它也匹配字符串的开头)。
preg_match_all('~(?:\G(?!\A)|<!-- @@include)\s+(try|default|file)="(.*?)"~', $str, $matches);
这种模式的想法是第一个匹配的第二个分支 <!-- @@include
成功,然后所有其他连续匹配的第一个分支 \G(?!\A)
成功。当 \s+(try|default|file)="(.*?)"
部分失败时,连续性被破坏,正则表达式引擎必须找到下一个出现的 <!-- @@include
才能继续。
如果想知道第二个分支什么时候成功,只需要在第二个分支中放一个捕获组即可:
$result = [];
if ( preg_match_all('~(?:\G(?!\A)|<!-- (@)@include)\s+(try|default|file)="(.*?)"~', $str, $matches, PREG_SET_ORDER) ) {
foreach ($matches as $m) {
if ( !empty($m[1]) ) { // test which branch succeeds
if ( isset($temp) )
$result[] = $temp;
$temp=[];
}
$temp[$m[2]] = $m[3];
}
}
if ( isset($temp) )
$result[] = $temp;
为了更灵活和能够处理未知键,你可以使用两个 preg_match_all
:
$result = [];
if ( preg_match_all('~<!-- @@include\s+\K\w+=".*?"(?:\s+\w+=".*?")*~', $str, $matches) ) {
foreach ($matches[0] as $params) {
if ( preg_match_all('~(\w+)="(.*?)"~', $params, $keyvals) )
$result[] = array_combine($keyvals[1], $keyvals[2]);
}
}
print_r($result);
请注意,最后一个解决方案对于大字符串可能更有效,特别是因为第一个模式不是以交替开头而是以文字字符串开头(在这种情况下,pcre 正则表达式引擎能够优化研究) .第二种模式只需要处理短字符串,所以这不是问题。
在 PHP 5.6/7.0 中工作。
我在几个正则表达式网站上尝试了几个问题和几个小时的几个正则表达式,但找不到任何东西可以满足我的需要。我有一个这样的字符串:
At vero eos et accusamus et iusto odio dignissimos ducimus
<!-- @@include default="/admin/creditapp/templates/longform/" try="/wTemplates/forms/templates/" file="credit_row_1.txt" -->
qui blanditiis praesentium voluptatum deleniti atque corrupti
<!-- @@include default="/admin/creditapp/templates/longform/" try="/wTemplates/forms/templates/" file="credit_row_2.txt" -->
quos dolores et quas excepturi sint
我正在从令牌中寻找以下匹配项:
<!-- @@include ...the whole thing... -->
default
/admin/creditapp/templates/longform
try
/wtemplates/forms/templates
file
credit_row_1.txt
每次找到整个组时自然重复。我可以循环文件并完成它,所以一次只需要一个实例就可以了。我能想出的唯一表达方式就是:
<!-- @@include (?:(try|default|file)=\"(.+?)\"?)(?:\s*)(?:(try|default|file)=\"(.+?)\"?)(?:\s*)(?:(try|default|file)=\"(.+?)\"?)(?:\s*)-->
这是巨大的,并且不允许其他可能性,比如,我不知道,“(try|foo|bar|default)”之类的,或者省略任何一个 "try" 或 "default," 例如“(foo|bar|文件)。”
在模板中
<!-- @@include -->
不变。里面可以是 2 到 n name=value 对。我试过了:
(<!-- @@include (?:(try|default|file)=\"(.+?)\" ?){1,3}-->)
但它只 returns 找到了姓氏=值。我想认为我很接近,但我无法解决。
PCRE 无法存储重复捕获组的不同内容。当组重复时,之前的内容被当前内容覆盖等等。
一种解决方法是使用 preg_match_all
并使用 \G
锚点匹配上一个匹配项之后的下一个位置(默认情况下它也匹配字符串的开头)。
preg_match_all('~(?:\G(?!\A)|<!-- @@include)\s+(try|default|file)="(.*?)"~', $str, $matches);
这种模式的想法是第一个匹配的第二个分支 <!-- @@include
成功,然后所有其他连续匹配的第一个分支 \G(?!\A)
成功。当 \s+(try|default|file)="(.*?)"
部分失败时,连续性被破坏,正则表达式引擎必须找到下一个出现的 <!-- @@include
才能继续。
如果想知道第二个分支什么时候成功,只需要在第二个分支中放一个捕获组即可:
$result = [];
if ( preg_match_all('~(?:\G(?!\A)|<!-- (@)@include)\s+(try|default|file)="(.*?)"~', $str, $matches, PREG_SET_ORDER) ) {
foreach ($matches as $m) {
if ( !empty($m[1]) ) { // test which branch succeeds
if ( isset($temp) )
$result[] = $temp;
$temp=[];
}
$temp[$m[2]] = $m[3];
}
}
if ( isset($temp) )
$result[] = $temp;
为了更灵活和能够处理未知键,你可以使用两个 preg_match_all
:
$result = [];
if ( preg_match_all('~<!-- @@include\s+\K\w+=".*?"(?:\s+\w+=".*?")*~', $str, $matches) ) {
foreach ($matches[0] as $params) {
if ( preg_match_all('~(\w+)="(.*?)"~', $params, $keyvals) )
$result[] = array_combine($keyvals[1], $keyvals[2]);
}
}
print_r($result);
请注意,最后一个解决方案对于大字符串可能更有效,特别是因为第一个模式不是以交替开头而是以文字字符串开头(在这种情况下,pcre 正则表达式引擎能够优化研究) .第二种模式只需要处理短字符串,所以这不是问题。