regex returns first and last match instead of returning first and second match inbetween matching parenthesis

Viewed 65

I am trying to parse text to extract desired strings. I am missing something in the regex, can someone help me figure out what is the issue here?

This is my script:

import re
a = """
    block1
          #(/*AUTOINSTPARAM*/
        // Parameters
        .THREE          (3),     // comment
        .TWO            (2), // comment
        .ONE    (1))             // comment
        inst1
           (/*AUTOINST*/
        // extra
        // output

    block2
          #(/*AUTOINSTPARAM*/
        // Parameters
        .THREE          (3),     // comment
        .TWO            (2), // comment
        .ONE    (1))             // comment
        inst2
           (/*AUTOINST*/
        // extra
        // output
"""

op = re.findall(r'(\w+)\s*(#\(.*\))?.*?(\w+)\s*\(', a, re.MULTILINE|re.DOTALL)
for i in op:
    print(i[0],i[2])

This is the output:

('block1', 'inst2')

Expected output:

('block1', 'inst1')
('block2', 'inst2')

Updated: Trying to test following input for the same regex as accepted answer:

import re
a = """
    except_check
          #(
            .a        (m),
            .b        (w),
            .c        (x),
            .d        (1),
            .e        (1)
        )
        data_check
           (// Outputs

  abc
  #(
    .a                          (b::c)
   )
   mask
   (/*AUTOINST*/

"""

op = re.findall(r'^\s*(\w+)\s*$\n(?:^\s*[#/.].*$\n)*^\s*(\w+)\s*\(', a, re.MULTILINE)
for i in op:
    print(i)

It did not return anything. It should have returned following:

('except_check', 'data_check')
('abc', 'mask')
3 Answers

Would you please try the following:

#op = re.findall(r'^\s*(\w+)\s*$\n(?:^\s*[#/.].*$\n)*^\s*(\w+)\s*\(', a, re.MULTILINE)
op = re.findall(r'^\s*(\w+)\s*$\n(?:^\s*[^\w\s].*$\n)*^\s*(\w+)\s*\(', a, re.MULTILINE)
for i in op:
    print(i)

Output:

('block1', 'inst1')
('block2', 'inst2')
  • ^\s*(\w+)\s*$\n matches the blockname line
  • (?:^\s*[^\w\s].*$\n)* matches the parameter lines
  • ^\s*(\w+)\s*\( matches the instance name line

Please note I have disabled the re.DOTALL option (although it is trivial to solve this problem).

The problem is the .* matching as much as possible (greedy) and with re.DOTALL it will match your entire string leavig as little as neccessary to still match at all.

(\w+)\s*(#\(.*\))?.*?(\w+)\s*\(
            ^^ this one

Basically any regex with .* (if . is allowed to capture truly everything) will only match once or not at all, since it is capable of matching anything the rest of the rexpression could aswell.

Just using .*? wont fix this either, because:

Another issue with the string are the brackets. Regex (without some fancy extensions) is only capable of matching brackets with finite nesting. Assuming a maximum nesting-depth of 2 in the AUTOINSTPARAM block, the following regex would work:

vvvvv blockX                                    vvvvv instX
(\w+)\s*(#\([^(]*(\([^)]*\)[^()]*)*\))?[^\n]*\s*(\w+)\s*\(
                  ^^inner^^
          ^^ outer bracket         ^^  

These [^()] groups are there instad of . to prevent it from eating any broken brackets by just ignoring them. If you know more about the format you could narrow this down even more.

Also note that this regex assumes the comment between the last ) and instX matches [^\n]*\s* and it will accept anything that is not a bracket inside brackets.

If the word characters for the second group are after 2 closing parenthesis, you can omit the re.DOTALL and use for example a pattern that stretches over multiple lines using [\s\S]*? matching as least as possible until you encounter 2 consecutive parenthesis.

This pattern is based on the example data, and can be error prone as it depends on 2 parenthesis as the last part before block 2.

^\s*(\w+)\r?\n\s*#\([\s\S]*?\)\s*\).*\r?\n\s*(\w+)

Regex demo

Another option could be matching the lines after the first block that do not start with a word character, and that capture in group 2 the word characters of the line that does.

^\s*(\w+)\r?\n[^\S\r\n]*#\(.*(?:\r?\n(?![^\S\r\n]*\w).*)*\r?\n[^\S\r\n]+(\w+)

Regex demo

Related