Regex : Match the two closest strings with only one occurence of another string between them

Viewed 61

I want to do a non-greedy match of two lines but that only include one occurence of another line between them.

Let me illustrate that with a log of ffprobe :

[FRAME]
media_type=video
stream_index=1
pict_type=P
coded_picture_number=1
display_picture_number=0
[/FRAME]
[FRAME]
media_type=video
stream_index=1
pict_type=B
coded_picture_number=1
display_picture_number=0
[/FRAME]
[FRAME]
media_type=video
stream_index=1
pict_type=P
coded_picture_number=1
display_picture_number=0
[/FRAME]
[FRAME]
media_type=video
stream_index=1
pict_type=I
coded_picture_number=1
display_picture_number=0
[/FRAME]
[FRAME]
media_type=video
stream_index=1
pict_type=P
coded_picture_number=1
display_picture_number=0
[/FRAME]
[FRAME]
media_type=video
stream_index=1
pict_type=I
coded_picture_number=1
display_picture_number=0
[/FRAME]

This log is composed of video frames. A Frame starts by [FRAME] and end by [/FRAME].

I want to match a pict_type=B immediately followed by pict_type=I in the next frame.

Obviously pict_type=B.*?pict_type=I don't work here, it'll match B P I

I tried to limit the occurence of [/FRAME] to only 1

pict_type=B(.*?[^\[\/FRAME\]]{1})pict_type=I

But it matches two [/FRAME] just to reach pict_type=I

Also each video have a different number of lines so it's useless to do a regex line with \r\n repeats

What did I do wrong and how can I tell it to allow only one [/FRAME] between my two pict_type ?

1 Answers

You could use a pattern that matches the opening [FRAME] and closing [/FRAME] and while matching first pict_type=B and then the second pict_type=I in the next frame not crossing those boundaries using a negative lookahead (?!

\[FRAME](?:\R(?!\[/?FRAME]|pict_type).*)*+\Rpict_type=B(?:\R(?!\[/?FRAME]|pict_type).*)*+\R\[/FRAME]\R\[FRAME](?:\R(?!\[/?FRAME]|pict_type).*)*+\Rpict_type=I(?:\R(?!\[/?FRAME]|pict_type).*)*+\R\[/FRAME]

The pattern matches:

  • \[FRAME] Match [FRAME]
  • (?:\R(?!\[/?FRAME]|pict_type).*)*+ Match all lines that do not start with [FRAME] or [/FRAME] or pict_type using a possessive quantifier *+
  • \Rpict_type=B Match a newline and pict_type=B
  • (?:\R(?!\[/?FRAME]|pict_type).*)*+ Match all lines that do not start with [FRAME] or [/FRAME] or pict_type
  • \R\[/FRAME] Match a newline and match the closing [/FRAME]
  • \R\[FRAME] Match a newline and match the second [FRAME]
  • (?:\R(?!\[/?FRAME]|pict_type).*)*+ Match all lines that do not start with [FRAME] or [/FRAME] or pict_type
  • \Rpict_type=I Match a newline and match pict_type=I
  • (?:\R(?!\[/?FRAME]|pict_type).*)*+ Match all lines that do not start with [FRAME] or [/FRAME] or pict_type
  • \R\[/FRAME] Match a newline and match [/FRAME]

Regex demo

enter image description here

Related