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 withhello
- 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
- Qtax trick (recently awarded an additional bounty by @AmalMurali)
- Replace a word with the number of the line on which it is found