具有重叠匹配的多行正则表达式

时间:2009-04-17 21:12:25

标签: c# .net css regex

我正在研究一种解析CSS样式声明文件的工具。它使用了一个非常复杂的正则表达式,除了预期的性能问题和一些暂时没有影响我的小错误之外,它正在做我想做的所有事情,除了一件事。

我有它匹配元素名称,类,子类,伪类等的所有组合。但是,当一行包含多个声明时,我只能匹配一次。举个例子,这就是目前绊倒我的事情:

td.class1, td.class2, td.class3
{
    background-color: #FAFAFA;
    height: 10px;
}

我可以编写一个表达式来满足这三个声明的所有要求,但是因为我也在它之后捕获信息(括号内的实际样式信息)我觉得这整个文本块被认为是计算的为此,引擎移动到刚刚处理完整个块之后的下一个字符。

有没有办法实现这一点,每个类将是一个单独的匹配,所有将包括后面的样式信息?我知道我可以修改我的正则表达式来匹配整行,然后在得到我的匹配后用逗号解析它,但是如果可能的话,我想把所有逻辑保留在表达式中。

我可以发布我用来生成它的表达式和/或注释代码,如果它与答案绝对相关,但表达式是巨大/丑陋的(因为所有非平凡的正则表达式都是)并且代码有点冗长。

5 个答案:

答案 0 :(得分:2)

你需要一个CSS解析器,而不是一个正则表达式。您应该阅读Is there a CSS Parser for C#

答案 1 :(得分:2)

这是一个适用于您的示例数据的正则表达式:

@"([^,{}\s]+(?:\s+[^,{}\s]+)*)(?=[^{}]*(\{[^{}]+\}))"

第一部分匹配并捕获组#1中的选择器(td.class1),然后前瞻跳过任何剩余的选择器并捕获组#2中的相关样式规则。下一次匹配尝试从前一次开始前瞻开始,因此它与下一个选择器(td.class2)匹配,并且前瞻再次捕获相同的规则块。

这不会处理@ -rules或注释,但它对您提供的示例数据工作正常。我甚至在一些真实世界的样式表上检查了它,并且它做得非常好。

答案 2 :(得分:1)

根据你的正则表达式引擎的深层细微差别,你可以通过在前瞻中嵌入捕获的parens来实现这一点,例如:

\.(\w+)(?=.*?{([^}]*)})

我希望找出匹配组的含义是一个很好的练习。

答案 3 :(得分:1)

这对正则表达式来说不是一个好问题。

另一方面,你只需要几次传递就可以编写一个基本的CSS解析器。

CSS语法只是[某些东西],[打开大括号],[其他一些东西],[关闭大括号]。

你找到了这两个块的东西,你用逗号分隔第一个,用分号分割第二个,你已经完成了。

答案 4 :(得分:0)

我需要对AmbroseChapel所说的内容采取类似的观点,我需要在AS3中使用它,所以我会分享它,以防它帮助其他人。我试图彻底,评论将引导您完成整个过程。我已经在一些流行的CSS锅炉板上进行了测试,并且效果很好。 :)(这仅用于列出选择器名称,而不是用于属性解析。)

    public function getSelectors( targetCSS:String, includeElements:Boolean = true ):ArrayCollection
    {

        var newSelectorCollection:ArrayCollection = new ArrayCollection();

        if( targetCSS == null || targetCSS == "" ) return newSelectorCollection;

        var newSelectors:Array = new Array();

        var elements:Array = new Array();
        var ids:Array = new Array();
        var classes:Array = new Array();

        // Remove comments
        var cssString:String = "";
        var commentParts:Array = targetCSS.split( "/*" );

        for( var c:int = 0; c < commentParts.length; c++ ){

            var comPart:String = commentParts[ c ] as String;

            var comTestArray:Array = comPart.split( "*/" );

            if( comTestArray.length > 1 ){

                comTestArray.shift();
                comPart = comTestArray.join( "" );

            }

            cssString += comPart;

        }

        // Remove \n
        cssString = cssString.split( "\n" ).join( "" );
        // Remove \t
        cssString = cssString.split( "\t" ).join( "" );
        // Split at }
        var cssParts:Array = cssString.split( "}" );

        for( var i:int = 0; i < cssParts.length; i++ ){

            var cssPrt:String = cssParts[ i ] as String;

            // Detect nesting such as media queries by finding more than one {
            var nestingTestArray:Array = cssPrt.split( "{" );

            // If there is nesting split at { then drop index 0 and re-join with {
            if( nestingTestArray.length > 2 ){

                nestingTestArray.shift();
                cssPrt = nestingTestArray.join( "{" );

            }

            // Split at each item at {
            var cssPrtArray:Array = cssPrt.split( "{" );

            // Disregard anything after {
            cssPrt = cssPrtArray[ 0 ] as String;

            // Split at ,
            var selectorList:Array = cssPrt.split( "," );

            for( var j:int = 0; j < selectorList.length; j++ ){

                var sel:String = selectorList[ j ] as String;

                // Split at : and only keep index 0
                var pseudoParts:Array = sel.split( ":" );

                sel = pseudoParts[ 0 ] as String;

                // Split at [ and only keep index 0
                var attrQuryParts:Array = sel.split( "[" );

                sel = attrQuryParts[ 0 ] as String;

                // Split at spaces
                var selectorNames:Array = sel.split( " " );

                for( var k:int = 0; k < selectorNames.length; k++ ){

                    var selName:String = selectorNames[ k ] as String;

                    if( selName == null || selName == "" ){

                        continue;

                    }

                    // Check for direct class applications such as p.class-name
                    var selDotIndex:int = selName.indexOf( ".", 1 );
                    if( selDotIndex != -1 ){

                        // Add the extra classes
                        var dotParts:Array = selName.split( "." );

                        for( var d:int = 0; d < dotParts.length; d++ ){

                            var dotPrt:String = dotParts[ d ] as String;

                            if( d > 0 ){

                                dotPrt = "." + dotPrt;

                                if( d == 1 && selName.indexOf( "." ) === 0 ){

                                    selName = dotPrt;

                                }else{

                                    selectorNames.push( dotPrt );

                                }

                            }else{

                                if( dotPrt != "" ){

                                    selName = dotPrt;

                                }

                            }

                        }

                    }

                    // Only add unique items
                    if( newSelectors.indexOf( selName ) == -1 ){

                        // Avoid @ prefix && avoid *
                        if( selName.charAt( 0 ) != "@" && selName != "*" ){

                            newSelectors.push( selName );

                            switch( selName.charAt( 0 ) ){

                                case ".":
                                    classes.push( selName );
                                    break;

                                case "#":
                                    ids.push( selName );
                                    break;

                                default:
                                    elements.push( selName );
                                    break;

                            }

                        }

                    }

                }

            }

        }

        if( includeElements ){

            newSelectorCollection.source = elements.sort().concat( ids.sort().concat( classes.sort() ) );

        }else{

            newSelectorCollection.source = ids.sort().concat( classes.sort() );

        }

        return newSelectorCollection;

    }