假设我有以下两个包含正则表达式的字符串。我如何合并它们?更具体地说,我希望将这两个表达式作为替代项。
$a = '# /[a-z] #i';
$b = '/ Moo /x';
$c = preg_magic_coalesce('|', $a, $b);
// Desired result should be equivalent to:
// '/ \/[a-zA-Z] |Moo/'
当然,将此作为字符串操作执行是不切实际的,因为这将涉及解析表达式、构建语法树、合并树,然后输出另一个与树等效的正则表达式。没有这最后一步我就很开心了。不幸的是,PHP 没有 RegExp 类(或者有吗?)。
有什么方法可以实现吗?顺便说一下,还有其他语言提供一种方法吗?这不是很正常的场景吗?可能不会。 :-(
或者,有没有一种方法可以有效地检查两个表达式中的任何一个是否匹配,以及哪个匹配得更早(如果它们在相同的位置匹配,则比赛更长)?这就是我现在正在做的事情。不幸的是,我经常在长字符串上针对两个以上的模式执行此操作。结果是慢(是的,这绝对是瓶颈)。
编辑:
我应该说得更具体——抱歉。 $a
和 $b
是变量,它们的内容不在我的控制范围内!否则,我会手动合并它们。因此,我不能对所使用的定界符或正则表达式修饰符做出任何假设。例如,请注意,我的第一个表达式使用了 i
修饰符(忽略大小写),而第二个表达式使用了 x
(扩展语法)。因此,我不能只是将两者连接起来,因为第二个表达式不忽略大小写并且第一个表达式不使用扩展语法(其中的任何空格都很重要!
最佳答案
我实际上看到了 porneL described一堆这样,但这可以解决大部分问题。它取消了先前子表达式中设置的修饰符(另一个答案遗漏了)并设置了每个子表达式中指定的修饰符。它还处理非斜线定界符(我在这里找不到允许字符的规范,所以我使用了。
,你可能想进一步缩小范围)。
一个弱点是它不处理表达式中的反向引用。我最担心的是反向引用本身的局限性。我将把它留给读者/提问者作为练习。
// Pass as many expressions as you'd like
function preg_magic_coalesce() {
$active_modifiers = array();
$expression = '/(?:';
$sub_expressions = array();
foreach(func_get_args() as $arg) {
// Determine modifiers from sub-expression
if(preg_match('/^(.)(.*)\1([eimsuxADJSUX]+)$/', $arg, $matches)) {
$modifiers = preg_split('//', $matches[3]);
if($modifiers[0] == '') {
array_shift($modifiers);
}
if($modifiers[(count($modifiers) - 1)] == '') {
array_pop($modifiers);
}
$cancel_modifiers = $active_modifiers;
foreach($cancel_modifiers as $key => $modifier) {
if(in_array($modifier, $modifiers)) {
unset($cancel_modifiers[$key]);
}
}
$active_modifiers = $modifiers;
} elseif(preg_match('/(.)(.*)\1$/', $arg)) {
$cancel_modifiers = $active_modifiers;
$active_modifiers = array();
}
// If expression has modifiers, include them in sub-expression
$sub_modifier = '(?';
$sub_modifier .= implode('', $active_modifiers);
// Cancel modifiers from preceding sub-expression
if(count($cancel_modifiers) > 0) {
$sub_modifier .= '-' . implode('-', $cancel_modifiers);
}
$sub_modifier .= ')';
$sub_expression = preg_replace('/^(.)(.*)\1[eimsuxADJSUX]*$/', $sub_modifier . '$2', $arg);
// Properly escape slashes
$sub_expression = preg_replace('/(?<!\\\)\//', '\\\/', $sub_expression);
$sub_expressions[] = $sub_expression;
}
// Join expressions
$expression .= implode('|', $sub_expressions);
$expression .= ')/';
return $expression;
}
编辑:我重写了这个(因为我是强制症)并以:
function preg_magic_coalesce($expressions = array(), $global_modifier = '') {
if(!preg_match('/^((?:-?[eimsuxADJSUX])+)$/', $global_modifier)) {
$global_modifier = '';
}
$expression = '/(?:';
$sub_expressions = array();
foreach($expressions as $sub_expression) {
$active_modifiers = array();
// Determine modifiers from sub-expression
if(preg_match('/^(.)(.*)\1((?:-?[eimsuxADJSUX])+)$/', $sub_expression, $matches)) {
$active_modifiers = preg_split('/(-?[eimsuxADJSUX])/',
$matches[3], -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
}
// If expression has modifiers, include them in sub-expression
if(count($active_modifiers) > 0) {
$replacement = '(?';
$replacement .= implode('', $active_modifiers);
$replacement .= ':$2)';
} else {
$replacement = '$2';
}
$sub_expression = preg_replace('/^(.)(.*)\1(?:(?:-?[eimsuxADJSUX])*)$/',
$replacement, $sub_expression);
// Properly escape slashes if another delimiter was used
$sub_expression = preg_replace('/(?<!\\\)\//', '\\\/', $sub_expression);
$sub_expressions[] = $sub_expression;
}
// Join expressions
$expression .= implode('|', $sub_expressions);
$expression .= ')/' . $global_modifier;
return $expression;
}
它现在使用 (?modifiers:sub-expression)
而不是 (?modifiers)sub-expression|(?cancel-modifiers)sub-expression
但是我'我们注意到两者都有一些奇怪的修饰符副作用。例如,在这两种情况下,如果子表达式具有 /u
修饰符,它将无法匹配(但如果您将 'u'
作为第二个参数传递新功能,匹配得很好)。
关于php - 在 PHP 中合并正则表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/244959/