views:

43

answers:

2

Hi there,

I'm writing a python script to loop through a directory of CSS files and save the contents of any which contain a specifically-formatted javadoc style comment.

The comment/CSS looks like this:

/**thirdpartycss

* @description Used for fixing stuff

*/
.class_one {
    margin: 10px;
}
#id_two {
    padding: 2px;
}

The regex to fetch the entire contents of the file looks like this:

pattern = "/\*\*thirdpartycss(.*?)}$"
matches = re.findall(pattern, css, flags=re.MULTILINE | re.DOTALL)

This gives me the file contents. What I want to do now is write a regex to grab each CSS definition within the class. This is what I tried:

rule_pattern = "(.*){(.*)}?"
rules = re.findall(rule_pattern, matches[0], flags=re.MULTILINE | re.DOTALL)

I'm basically trying to find any text, then an opening {, any text, then a closing } - I want a list of all of the CSS classes, essentially, but this just returns the entire string in one chunk.

Can anybody point me in the right direction?

Thanks. Matt

+2  A: 

{(.*)} is a greedy match -- it will match from the first { to the last }, thus gobble up any {/} pairs that might be inside those. You want non-greedy matching, that is

{(.*?)}

the difference is the question mark after the asterisk, making it non-greedy.

This still won't work if you need to properly match "nested" braces -- but then, nothing in the RE world will: among regular languages many well-known limitations (regular languages are those that regular expressions can match) is that "properly nesting" any kind of open/closed parentheses is impossible (some incredibly-extended so-called-RE manage to, but not Python's, and anybody with CS background will find calling those expression "regular" offensive anyway;-). If you need more general parsing than REs can afford, pyparsing or other full-fledged Python parsers are the right way to go.

Alex Martelli
+1 because pyparsing is a Good Thing™.
jathanism
@jathanism, is that a "virtual +1"? Because 5 minutes after this comment of yours I see my A still with 0 upvotes!-)
Alex Martelli
Haha! I usually comment then vote. Good that you're on the case. ;-)
jathanism
Thanks, and sorry for the delay in accepting this - been busy with other projects.
Matt Andrews
+1  A: 

@Alex is right (is he ever not? but I digress). You are better off using a custom parser if you need more specific parsing than what regular expressions can offer. Luckily you don't have to reinvent the (CSS parsing) wheel. There is an already existing solution for this.

I faced a similar requirement some time back. The cssutils module came in handy at the time. I just refreshed my cssutils fu to cook up this code snippet for you:

In [16]: import cssutils

In [17]: s = """/**thirdpartycss
* @description Used for fixing stuff
*/
.class_one {
    margin: 10px;
}
#id_two {
    padding: 2px;
}"""

In [26]: sheet = cssutils.parseString(s)

In [27]: sheet.cssRules
Out[27]: 
[cssutils.css.CSSComment(cssText=u'/**thirdpartycss\n* @description Used for fixing stuff\n*/'),
 cssutils.css.CSSStyleRule(selectorText=u'.class_one', style=u'margin: 10px'),
 cssutils.css.CSSStyleRule(selectorText=u'#id_two', style=u'padding: 2px')]

In [28]: sheet.cssRules[0].cssText
Out[28]: u'/**thirdpartycss\n* @description Used for fixing stuff\n*/'

In [29]: print sheet.cssRules[0].cssText
-------> print(sheet.cssRules[0].cssText)
/**thirdpartycss
* @description Used for fixing stuff
*/

You can parse the CSS and then loop through the sheet object's cssRules to find all CSSComment instances.

Manoj Govindan
This looks really useful - I'm marking Alex's answer as the correct one as it more directly solves my issue, but I wasn't aware of that module and I'll definitely take a look. Really appreciate the code sample too - thank you sir.
Matt Andrews