我有一个简单的字符串:
$string = '--#--%--%2B--';
我想对所有字符(包括"孤独"的%
)进行百分比编码,除了-
字符和形式为%xy
的三元组。所以我写了以下模式替代方案:
$pattern1 = '/(?:[-]+|%[A-Fa-f0-9]{2})(*SKIP)(*FAIL)|./us';
$pattern2 = '/(?:[-]+)(*SKIP)(*FAIL)|(?:%[A-Fa-f0-9]{2})(*SKIP)(*FAIL)|./us';
请注意使用(多个)(*SKIP)(*FAIL)
和(?:)
。
匹配和替换的结果是相同的,也是正确的:
--%23--%25--%2B--
我想问:
- 这两种模式等效吗?如果不是,哪一个应该是正确的url编码?你能简单解释一下为什么吗
- 你会建议其他替代方案吗(暗示回溯控制动词),或者我的模式是个好选择吗
- 即使(多个)
(*SKIP)(*FAIL)
在整个(选定的)模式中,我也可以只应用一个(?:)
吗
我知道我一次问了更多的问题,对你的要求有点太高了。请接受我的道歉!非常感谢。
p.S:我已经用以下PHP代码进行了测试:
$result = preg_replace_callback($patternX, function($matches) {
return rawurlencode($matches[0]);
}, $string);
echo $result;
首先,这两种模式都利用了SKIP-FAIL PCRE动词序列,这是一个众所周知的"技巧"来匹配某些文本并跳过。请参阅(*SKIP)或(*F)如何处理regex?了解更多详细信息。
这两种模式产生相同的结果,(?:[-]+|%[A-Fa-f0-9]{2})(*SKIP)(*FAIL)
匹配[-]+
或%[A-Fa-f0-9]{2}
,然后跳过匹配,(?:[-]+)(*SKIP)(*FAIL)|(?:%[A-Fa-f0-9]{2})(*SKIP)(*FAIL)
首先尝试匹配[-]+
,如果找到则跳过,然后尝试匹配%[A-Fa-f0-9]{2}
,如果找到,则跳过匹配。第二模式中的(?:...)
非捕获组是冗余的,因为内部没有交替并且组没有被量化。您可以在模式中使用任意数量的(*SKIP)(*FAIL)
,只需确保在|
之前使用它们即可跳过相关匹配。
当你想在特定的上下文中匹配一些文本时,当一个字符前面是和后面是一些字符时,应该跳过/"避免"一个字符时,或者当你需要"避免"匹配整个字符序列时,比如在这种情况下,SKIP-FAIL技术是很好的使用方法。