使用字符串开头和可选结尾的正则表达式多和/或单行匹配

时间:2016-12-23 15:16:56

标签: php regex preg-replace

这是一个棘手的问题,我没有发现任何明确的迹象表明这是否可行; 匹配从起点指示到行尾(一行匹配)的所有内容(包括),除非在另一个起点之前有一个终点指示,在这种情况下匹配所有内容并包括它(多行匹配) )

假设我们有$ str =

blah blah begin 12345
bleh bleh
begin test

我们可以轻松匹配,例如使用begin 12345删除preg_replace('@begin(.*?)@i', "", $str);,向我们提供结果:

blah blah 
bleh bleh

如果我们改为$ str =

blah blah begin 12345
bleh finish bleh begin test

我们还可以使用begin轻松删除finishpreg_replace('@begin(.*?)finish@is', "", $str);之间的所有内容,并向我们提供结果blah blah bleh begin test

以这种方式使用s选项,我们可以轻松地匹配 整行或多行。但是,如果找不到finish,那么我们该如何匹配单行,直到另一个begin,否则多行包括beginfinish

所以,例如,如果你有$ str:

1 begin 2
3 begin 4
5 finish 6
7 finish 8
9 begin 10

如何使用单个preg_replace()删除begin(.*?)(finish)?之类的内容以获得以下预期输出?

1 
3  6
7 finish 8
9 

请注意3仍然存在,因为第一个“begin - 超越”匹配并非贪婪,但5被删除,因为finish来自另一个begin 1}}。但7 finish仍然存在,因为它没有begin ning。这甚至可能吗?

1 个答案:

答案 0 :(得分:2)

这完全有可能,但有点棘手 - 您可以使用以下正则表达式实现此目的:begin(?s)((?!finish|begin).)*finish|begin(?-s).*

让我们来看看正则表达式。它使用替换,其中第一个替代匹配所有场合,其中begin符合结束finish,使用tempered greedy token和内联单线修改器。第二种方法匹配剩余的begin并删除单行模式。

  • begin - 匹配字符串begin
  • (?s) - 在
  • 上启用单线模式
  • ((?!finish|begin).)* - 匹配任何未开始的字符数beginfinish
  • finish - 匹配字符串finish
  • | - 开始更改
  • begin - 匹配刺痛begin(因此尚未匹配的所有begin
  • (?-s) - 关闭单线模式
  • .* - 匹配该行的提醒

请参阅demo

驯化贪婪的代币并不是非常有效,因为必须检查每一个字符的前瞻,但我们可以控制它以提高效率。由于注册版本在第一次交替中使用了否定的字符类并且没有更多的点匹配,我们也可以删除内联修饰符。

begin(?:[^bf]*(?:(?:b(?!egin)|f(?!inish))[^bf]*)*)finish|begin.*
  • [^bf]* - 匹配任意数量的字符,既不是b也不是f
  • (?:b(?!egin)|f(?!inish))[^bf]*+)* - 匹配bf不属于不需要的字词,然后是其他非bf字符 - 重复零次或多次。
  • 内部有一个强大的修饰符*+,以避免不必要的回溯到不匹配案例的模式。

另一个demo