我正在研究一种解析CSS样式声明文件的工具。它使用了一个非常复杂的正则表达式,除了预期的性能问题和一些暂时没有影响我的小错误之外,它正在做我想做的所有事情,除了一件事。
我有它匹配元素名称,类,子类,伪类等的所有组合。但是,当一行包含多个声明时,我只能匹配一次。举个例子,这就是目前绊倒我的事情:
td.class1, td.class2, td.class3
{
background-color: #FAFAFA;
height: 10px;
}
我可以编写一个表达式来满足这三个声明的所有要求,但是因为我也在它之后捕获信息(括号内的实际样式信息)我觉得这整个文本块被认为是计算的为此,引擎移动到刚刚处理完整个块之后的下一个字符。
有没有办法实现这一点,每个类将是一个单独的匹配,所有将包括后面的样式信息?我知道我可以修改我的正则表达式来匹配整行,然后在得到我的匹配后用逗号解析它,但是如果可能的话,我想把所有逻辑保留在表达式中。
我可以发布我用来生成它的表达式和/或注释代码,如果它与答案绝对相关,但表达式是巨大/丑陋的(因为所有非平凡的正则表达式都是)并且代码有点冗长。
答案 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;
}