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

Regex multi- AND/OR single-line match using a string beginning and optional ending

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

假设我们有 $str =

blah blah begin 12345
bleh bleh
begin test

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

blah blah 
bleh bleh

如果我们改为 $str =

blah blah begin 12345
bleh finish bleh begin test

我们还可以使用 preg_replace('@begin(.*?)finish@is', "", $str); 轻松删除 beginfinish 之间的所有内容,得到结果 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-and-beyond”匹配是非贪婪的,但 5 被删除,因为 finish 出现在另一个之前begin。但是 7 finish 仍然存在,因为它没有 beginning。这甚至可能吗?

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

让我们看一下正则表达式。它使用交替,其中第一个替代匹配所有场合,其中 begin 遇到结束 finish,使用 tempered greedy token 和内联单行修饰符。第二种选择匹配剩余的 begins 并删除单行模式。

  • begin - 匹配字符串 begin
  • (?s) - 打开单行模式
  • ((?!finish|begin).)* - 匹配不以 beginfinish
  • 开头的任意数量的字符
  • finish - 匹配字符串 finish
  • | - 开始交替
  • begin - 匹配字符串 begin(因此所有尚未匹配的 begin
  • (?-s) - 关闭单行模式
  • .* - 匹配行
  • 的提醒

demo

Tempered greedy tokens 不是很有效,因为必须检查每个字符的前瞻性,但我们可以加入它以提高效率。由于注册版本使用否定字符 类 并且在第一个交替中不再使用点匹配,我们也可以删除内联修饰符。

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

另一个demo