Kon*_*lph 10 php regex abstract-syntax-tree
假设我有以下两个包含正则表达式的字符串.我如何合并它们?更具体地说,我希望将这两个表达式作为替代.
$a = '# /[a-z] #i';
$b = '/ Moo /x';
$c = preg_magic_coalesce('|', $a, $b);
// Desired result should be equivalent to:
// '/ \/[a-zA-Z] |Moo/'
Run Code Online (Sandbox Code Playgroud)
当然,这样做作为字符串操作是不实际的,因为它涉及解析表达式,构造语法树,合并树然后输出相当于树的另一个正则表达式.没有这最后一步,我真的很开心.不幸的是,PHP没有RegExp类(或者是吗?).
有没有办法实现这个目标?顺便说一句,是否有其他语言提供方式?这不是一个很正常的场景吗?可能不会.:-(
或者,是否有一种方法可以有效地检查两个表达式中的任何一个是否匹配,哪一个匹配更早(如果它们在同一位置匹配,哪个匹配更长)?这就是我现在正在做的事情.不幸的是,我经常在长字符串上执行此操作,对于两种以上的模式.结果很慢(是的,这肯定是瓶颈).
我应该更具体 - 抱歉.$a并且$b是变量,它们的内容超出了我的控制范围!否则,我会手动合并它们.因此,我不能对使用的分隔符或正则表达式修饰符做任何假设.请注意,例如,我的第一个表达式使用i修饰符(忽略大小写),而第二个表达式使用x(扩展语法).因此,我不能只将两者连接起来,因为第二表达并不能忽视壳体和第一不使用扩展语法(和任何空白在其中是显著!
我看到porneL 实际上描述了很多这样的内容,但这解决了大部分问题。它取消先前子表达式中设置的修饰符(另一个答案错过了)并设置每个子表达式中指定的修饰符。它还处理非斜杠分隔符(我找不到此处允许使用哪些字符的规范,因此我使用了.,您可能需要进一步缩小范围)。
一个弱点是它不处理表达式内的反向引用。我对此最担心的是反向引用本身的局限性。我将把它作为练习留给读者/提问者。
// 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;
}
Run Code Online (Sandbox Code Playgroud)
编辑:我重写了这个(因为我是强迫症)并最终得到:
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;
}
Run Code Online (Sandbox Code Playgroud)
它现在使用(?modifiers:sub-expression)而不是(?modifiers)sub-expression|(?cancel-modifiers)sub-expression但我注意到两者都有一些奇怪的修饰符副作用。例如,在这两种情况下,如果子表达式具有修饰符/u,它将无法匹配(但如果您'u'作为新函数的第二个参数传递,则匹配得很好)。
| 归档时间: |
|
| 查看次数: |
1130 次 |
| 最近记录: |