How to take care of inner tag in regex?

Viewed 26

I try to parse CSS by regex (PHP preg_match_all) like

([^{]+)\s*\{\s*([^}]+)\s*}

but the problem is when there is an inner { tag. For example,

table {
    border-collapse: collapse;
    border-spacing: 0
}

@keyframes blinking {
    from {
        background: #f4a83d
    }
    to {
        background: rgba(244, 168, 61, 0)
    }
}

I need to parse it to two elements, but the result is

Array
(
    [0] => Array
        (
            [0] => 
table {
    border-collapse: collapse;
    border-spacing: 0
}
            [1] => 

@keyframes blinking {
    from {
        background: #f4a83d
    }
            [2] => 
    to {
        background: rgba(244, 168, 61, 0)
    }
        )

    [1] => Array
        (
            [0] => 
table 
            [1] => 

@keyframes blinking 
            [2] => 
    to 
        )

    [2] => Array
        (
            [0] => border-collapse: collapse;
    border-spacing: 0

            [1] => from {
        background: #f4a83d
    
            [2] => background: rgba(244, 168, 61, 0)
    
        )

)

NOTE: There are several classes and programs for parsing CSS, but they are an overkill for what I need to just capture the selector and the rules. They parse all the rules, which I need to re-join. Therefore, it is more convenient to use a simple regex approach instead.

1 Answers

Use

([^{]+)\s*({\s*((?:[^{}]++|(?2))*)\s*})

See proof

Explanation

--------------------------------------------------------------------------------
  (                        group and capture to \1:
--------------------------------------------------------------------------------
    [^{]+                    any character except: '{' (1 or more
                             times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
  )                        end of \1
--------------------------------------------------------------------------------
  \s*                      whitespace (\n, \r, \t, \f, and " ") (0 or
                           more times (matching the most amount
                           possible))
--------------------------------------------------------------------------------
  (                        group and capture to \2:
--------------------------------------------------------------------------------
    {                        '{'
--------------------------------------------------------------------------------
    \s*                      whitespace (\n, \r, \t, \f, and " ") (0
                             or more times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
    (                        group and capture to \3:
--------------------------------------------------------------------------------
      (?:                      group, but do not capture (0 or more
                               times (matching the most amount
                               possible)):
--------------------------------------------------------------------------------
        [^{}]++                   any character except: '{', '}' (1 or
                                 more times (matching the most amount
                                 possible possessively))
--------------------------------------------------------------------------------
       |                        OR
--------------------------------------------------------------------------------
        (                        group and capture to \4:
--------------------------------------------------------------------------------
          (?2)                   Capturing group 2 pattern recursed
--------------------------------------------------------------------------------
        )                        end of \4
--------------------------------------------------------------------------------
      )*                       end of grouping
--------------------------------------------------------------------------------
    )                        end of \3
--------------------------------------------------------------------------------
    \s*                      whitespace (\n, \r, \t, \f, and " ") (0
                             or more times (matching the most amount
                             possible))
--------------------------------------------------------------------------------
    }                        '}'
--------------------------------------------------------------------------------
  )                        end of \2
Related