Многострочное регулярное выражение с перекрывающимися совпадениями - PullRequest
1 голос
/ 18 апреля 2009

Я работаю над инструментом, который анализирует файлы для объявлений стиля CSS. Он использует очень сложное регулярное выражение, которое, помимо ожидаемых проблем с производительностью и нескольких незначительных ошибок, которые пока не затрагивают меня, делает все, что я хотел бы, за исключением одной вещи.

У меня он соответствует всем комбинациям имен элементов, классов, подклассов, псевдоклассов и т. Д. Однако, когда строка содержит более одного объявления, я могу получить его только один раз. В качестве примера, вот такая вещь, которая сбивает меня с толку в данный момент:

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

Я могу написать выражение, которое удовлетворит это для всех трех объявлений, но так как я также собираю информацию после нее (фактическую информацию о стиле в скобках), я чувствую, что весь этот блок текста считается учтенным для этого двигатель переходит к следующему символу после всего блока, который был только что обработан.

Есть ли способ сделать это, когда каждый класс будет иметь отдельное совпадение, и все будут также включать информацию о стиле, которая следует за этим? Я знаю, что могу изменить свое регулярное выражение, чтобы оно соответствовало всей строке, а затем проанализировать его на запятые после получения соответствия, но я бы хотел сохранить всю свою логику внутри самого выражения, если это возможно.

Я могу опубликовать выражение и / или закомментированный код, который я использую, чтобы сгенерировать его, если оно абсолютно уместно для ответа, но выражение является огромным / уродливым (как все нетривиальные регулярные выражения), а код немного длинен ,

Ответы [ 5 ]

2 голосов
/ 18 апреля 2009

Вот регулярное выражение, которое работает с вашими примерами данных:

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

Первая часть сопоставляет и захватывает селектор (td.class1) в группе # 1, затем упреждающий просмотр пропускает все оставшиеся селекторы и захватывает связанные правила стиля в группе # 2. Следующая попытка сопоставления начинается с того места, где стартовая точка запускалась в предыдущий раз, поэтому она совпадает со следующим селектором (td.class2), и эта команда снова захватывает тот же блок правил.

Это не будет обрабатывать @ -rules или комментарии, но отлично работает с предоставленными вами примерами данных. Я даже проверил это на некоторых реальных таблицах стилей, и это было замечательно хорошо.

2 голосов
/ 18 апреля 2009

Вам нужен анализатор CSS, а не регулярное выражение. Вы, вероятно, должны прочитать Есть ли парсер CSS для C # .

1 голос
/ 18 апреля 2009

Это не очень хорошая проблема для регулярных выражений.

С другой стороны, для написания базового синтаксического анализатора CSS вам понадобится всего пара проходов.

Синтаксис CSS - это просто [некоторые вещи], [открытая фигурная скобка], [некоторые другие вещи], [закрыть фигурные скобки] в конце концов.

Вы находите эти два куска материала, вы разделяете первый на запятые, а второй на точки с запятой, и вы почти закончили.

1 голос
/ 18 апреля 2009

В зависимости от глубоких нюансов вашего движка регулярных выражений, вы можете сделать это, внедрив захват паренсов в заголовки, то есть что-то вроде:

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

Я бы ожидал, что выяснение значения групп совпадений будет довольно хорошим упражнением.

0 голосов
/ 18 января 2013

Мне нужно было взглянуть аналогично тому, что сказал 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;

    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...