Reputation: 200
I have a multi-line string with given representation:
text1 (arbitrary chars and lines)\n
<hr>\n
Bitmap: ./media/logo.bmp\n
text2 (arbitrary chars and lines)\n
text3 (arbitrary chars and lines)\n
<hr>\n
Bitmap: ./media/logo.bmp\n
text2 (arbitrary chars lines)\n
\n
I want to match this substring that always occures twice in the string (once always in the end):
<hr>\n
Bitmap: ./media/logo.bmp\n
text2 (arbitrary chars and lines)\n
When I try to match with re.search
, it returns the long match:
regex = re.compile('<hr>\n'
'Bitmap: [\S\n ]*'
'$')
print(re.search(regex, string).group())
>> '<hr>\nBitmap: ./media/logo.bmp\ntext2 (arbitrary chars and lines)\ntext3 (arbitrary chars and lines)\n<hr>\nBitmap: ./media/logo.bmp\ntext2 (arbitrary chars and lines)\n\n'
Is it possible to use regex
to find the short match?
Solution:
Lookahead with OR operator returns two matches (one longer and one shorter):
regex = re.compile('<hr>\n'
'Bitmap: [\S]*\n'
'[\s\S]*?(?=<hr>|\n\Z)')
print(re.findall(regex, string))
>> ['<hr>\nBitmap: ./media/logo.bmp\ntext2 (arbitrary chars and lines)\ntext3 (arbitrary chars and lines)\n', '<hr>\nBitmap: ./media/logo.bmp\ntext2 (arbitrary chars lines)\n']
Upvotes: 0
Views: 356
Reputation: 18611
Use
(?m)^<hr>\r?\nBitmap:[\s\S]*?(?=^<hr>$|\Z)
See proof.
Explanation
--------------------------------------------------------------------------------
(?m) set flags for this block (with ^ and $
matching start and end of line) (case-
sensitive) (with . not matching \n)
(matching whitespace and # normally)
--------------------------------------------------------------------------------
^ the beginning of a "line"
--------------------------------------------------------------------------------
<hr> '<hr>'
--------------------------------------------------------------------------------
\r? '\r' (carriage return) (optional (matching
the most amount possible))
--------------------------------------------------------------------------------
\n '\n' (newline)
--------------------------------------------------------------------------------
Bitmap: 'Bitmap:'
--------------------------------------------------------------------------------
[\s\S]*? any character of: whitespace (\n, \r, \t,
\f, and " "), non-whitespace (all but \n,
\r, \t, \f, and " ") (0 or more times
(matching the least amount possible))
--------------------------------------------------------------------------------
(?= look ahead to see if there is:
--------------------------------------------------------------------------------
^ the beginning of a "line"
--------------------------------------------------------------------------------
<hr> '<hr>'
--------------------------------------------------------------------------------
$ before an optional \n, and the end of a
"line"
--------------------------------------------------------------------------------
| OR
--------------------------------------------------------------------------------
\Z the end of the string
--------------------------------------------------------------------------------
) end of look-ahead
Upvotes: 2
Reputation: 3434
This works: <hr>\nBitmap:.*\n(?:.*\n){1,2}
See: https://regex101.com/r/i64K0W/3
The problem in your regex was the *
, which is greedy.
Upvotes: 0