Capture a specific line if it exists with regex

Viewed 50

If I have files that could either be:

numbers:
32
45
999

56

or

numbers:
23
45

56

The 999 is a constant, but the other numbers & number of lines change.

Is there a way of capturing:

  • above 999 followed by an empty line in the first case (i.e. excluding 999)
  • above the empty line in the second case, where 999 doesn't exist

So far I've tried:

  • (numbers:(?:\n.+)*)(\n999) — this works great in the first case; the first group captures everything above 999. It obv doesn't work where there's no 999, so...
  • (numbers:(?:\n.+)*)(\n999)? — I would have thought this would work for both cases. But in the first case, this captures the 999 in the first group, I guess because it's greedy and the ? makes the (\n999) optional, so the first group is free to capture it.

It's also possible I'm massively overcomplicating this and there's some easy solution.

Thanks a lot!

2 Answers

Here is a regex that does it without using lookahead:

^(numbers:(?:\n.+)*?)(?:\n(?:999)?$)

RegEx Demo

RegEx Details:

  • ^: Start line
  • (: Start capture group #1
    • numbers:: Match numbers: text
    • (?:\n.+)*?: Match a line break followed by 1+ character in a line. Repeat this 0 or more times (non-greedy)
  • ): End capture group #1
  • (?:\n(?:999)?$): Must be followed by a line break and 999 or empty line

Use

(numbers:(?:\n(?!999).+)*)(\n999)?

See regex proof.

The question mark was the right move. The (?!999) negative lookahead is restricting the (.+)* and it does not cross optional nines.

If you do not want cross another numbers add to lookahead:

(numbers:(?:\n(?!999|numbers:).+)*)(\n999)?

EXPLANATION

--------------------------------------------------------------------------------
  (                        group and capture to \1:
--------------------------------------------------------------------------------
    numbers:                 'numbers:'
--------------------------------------------------------------------------------
    (?:                      group, but do not capture (0 or more
                             times (matching the most amount
                             possible)):
--------------------------------------------------------------------------------
      \n                       '\n' (newline)
--------------------------------------------------------------------------------
      (?!                      look ahead to see if there is not:
--------------------------------------------------------------------------------
        999                      '999'
--------------------------------------------------------------------------------
      )                        end of look-ahead
--------------------------------------------------------------------------------
      .+                       any character except \n (1 or more
                               times (matching the most amount
                               possible))
--------------------------------------------------------------------------------
    )*                       end of grouping
--------------------------------------------------------------------------------
  )                        end of \1
--------------------------------------------------------------------------------
  (                        group and capture to \2 (optional
                           (matching the most amount possible)):
--------------------------------------------------------------------------------
    \n                       '\n' (newline)
--------------------------------------------------------------------------------
    999                      '999'
--------------------------------------------------------------------------------
  )?                       end of \2 (NOTE: because you are using a
                           quantifier on this capture, only the LAST
                           repetition of the captured pattern will be
                           stored in \2)

You prefer no lookarounds? Here:

^(numbers:[\s\S]*?)\n(?:999)?$

See proof.

EXPLANATION

--------------------------------------------------------------------------------
  ^                        the beginning of the string
--------------------------------------------------------------------------------
  (                        group and capture to \1:
--------------------------------------------------------------------------------
    numbers:                 'numbers:'
--------------------------------------------------------------------------------
    [\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))
--------------------------------------------------------------------------------
  )                        end of \1
--------------------------------------------------------------------------------
  \n                       '\n' (newline)
--------------------------------------------------------------------------------
  (?:                      group, but do not capture (optional
                           (matching the most amount possible)):
--------------------------------------------------------------------------------
    999                      '999'
--------------------------------------------------------------------------------
  )?                       end of grouping
--------------------------------------------------------------------------------
  $                        before an optional \n, and the end of the
                           string
Related