正则表达式:捕获第一个可选字符串之前的所有内容

11

我想用 preg_match 捕获一个模式,直到但不包括第一个可选的其他模式实例,例如:

ABCDEFGwTW$%                         | capture ABCD
@Q%HG@H%hg afdgwsa g   weg#D DEFG    | capture @Q%HG@H%hg afdgwsa g   weg#D D
@Q%HDEFG@H%hg afdgwsa g   weg#D DEFG | capture @Q%HD

因此,在上述情况中,会捕获字符串EFG第一次出现之前的任何内容。同时,如果不存在EFG字符串,则希望捕获整个字符串。

我原本以为以下方式可以实现,但并没有成功:

$pattern = '/(.*)(?:EFG)?/';
preg_match($pattern, 'Q$TQ@#%GEFGw35hqb', $matches);
print_r($matches);
//should give: 'Q$TQ@#%G'
5个回答

28

你可以使用

'/(.*?)(?=EFG|$)/'

啊,非常好 - 我没有想到匹配EFG或行尾 :) - mulllhausen
老兄,你是我的英雄。 - Jay Yang

4
尝试这个:(.*?)(?:EFG|$) 它将匹配任何字符(尽可能少),直到找到EFG。

问题在于它无法匹配不包含EFG的字符串。 - mulllhausen
好的,哎呀!也要匹配行尾字符。 (更新的答案。) - Josh M.
抱歉,@Josh M.,@Jens已经完成了:P - mulllhausen
没关系,是我错过了那个要求。 - Josh M.
作为一条注释,即使更新后的版本也不正确:它实际上在结果中包括了可选字符串(而问题是“捕获第一个可选字符串之前的所有内容”)。 - Samuël Visser
匹配的内容包括可选字符串,但重要部分是从第一个捕获组中获取的,例如 RegExp.exec(/(.*?)(?:EFG|$)/, 'Q$TQ@#%GEFGw35hqb')[1] 可以仅获取 "Q$TQ@#%G"。 - Josh M.

1

另一种方法:

$str = 'Q$TQ@#%GEFGw35hqb';
$res = preg_split('/EFG/', $str);
print_r($res);

使用list($match) = preg_split('/EFG/', $haystack)将字符串直接捕获到变量中 :) - mulllhausen
使用带有字面模式的 preg_ 调用是不必要的开销。为什么不直接使用 explode()EFG - mickmackusa

0

你可以更少地混淆就得到结果:

只需检查一个更简单的匹配模式,如果不行,则使用原始字符串:

<?php
$match = 'Q$TQ@#%GEFGw35hqb';
if (preg_match('/^(.*)EFG/', $match, $matches)) {
    $match = $matches[1];
}

echo $match;

是的,我也可以用 stripos 做类似的事情,但我想学习一些关于正则表达式的知识——我认为这更整洁。 - mulllhausen
是的 ;) 不过,如果性能是一个问题,你应该使用 strpos ;) - Yoshi

0

使用带有惰性匹配和前瞻的模式的 preg_match() 比仅使用贪婪匹配(和没有前瞻)的 preg_replace() 并将可选匹配替换为空字符串要花费更多步骤。如果针不存在,则字符串中不会发生任何更改。非常简单。

代码:(演示)

$strings = [
    'ABCDEFGwTW$%',
    '@Q%HG@H%hg afdgwsa g   weg#D DEFG',
    '@Q%HDEFG@H%hg afdgwsa g   weg#D DEFG',
    'No needle in the haystack',
];

var_export(preg_replace('/EFG.*/', '', $strings));

输出:

array (
  0 => 'ABCD',
  1 => '@Q%HG@H%hg afdgwsa g   weg#D D',
  2 => '@Q%HD',
  3 => 'No needle in the haystack',
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接