Skip to content
Advertisement

On Which Line Number Was the Regex Match Found?

I would like to search a .java file using Regular Expressions and I wonder if there is a way to detect one what lines in the file the matches are found.

For example if I look for the match hello with Java regular expressions, will some method tell me that the matches were found on lines 9, 15, and 30?

Advertisement

Answer

Possible… with Regex Trickery!

Disclaimer: This is not meant to be a practical solution, but an illustration of a way to use an extension of a terrific regex hack. Moreover, it only works on regex engines that allow capture groups to refer to themselves. For instance, you could use it in Notepad++, as it uses the PCRE engine—but not in Java.

Let’s say your file is:

some code
more code
hey, hello!
more code

At the bottom of the file, paste :1:2:3:4:5:6:7, where : is a delimiter not found in the rest of the code, and where the numbers go at least as high as the number of lines.

Then, to get the line of the first hello, you can use:

(?m)(?:(?:^(?:(?!hello).)*(?:r?n))(?=[^:]+((?(1)1):d+)))*.*hello(?=[^:]+((?(1)1)+:(d+)))

The line number of the first line containing hello will be captured by Group 2.

  • In the demo, see Group 2 capture in the right pane.
  • The hack relies on a group referring to itself. In the classic @Qtax trick, this is done with (?>1?). For diversity, I used a conditional instead.

Explanation

  • The first part of the regex is a line skipper, which captures an increasing amount of the the line counter at the bottom to Group 1
  • The second part of the regex matches hello and captures the line number to Group 2
  • Inside the line skipper, (?:^(?:(?!hello).)*(?:r?n)) matches a line that doesn’t contain hello.
  • Still inside the line skipper, the (?=[^:]+((?(1)1):d+)) lookahead gets us to the first : with [^:]+ then the outer parentheses in ((?(1)1):d+)) capture to Group 1… if Group 1 is set (?(1)1) then Group 1, then, regardless, a colon and some digits. This ensures that each time the line skipper matches a line, Group 1 expands to a longer portion of :1:2:3:4:5:6:7
  • The * mataches the line skipper zero or more times
  • .*hello matches the line with hello
  • The lookahead (?=[^:]+((?(1)1)+:(d+))) is identical to the one in the line skipper, except that this time the digits are captured to Group 2: (d+)

Reference

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement