求一个递归匹配的正则表达式

BestAns1 2008-11-08 02:57:42
正则表达式,,,唉,真方便,也真挠头~~

要匹配 <%IF%> ... <%ELSE%> ... <%/IF%> 这种情况。如果第一个 IF 有完整的匹配,则返回完整的匹配字符串;如果 IF 不完整,则返回空。

举例一:
以下内容正确,返回全部:
<%IF 3+2=5%>
<%IF
"hello"="hello"%>
asdf
<%/IF%>
<%ELSE%>
12
34
<%/IF%>

举例二:
以下内容 IF 不完整,返回空:
<%IF 3+3=5%>asfdsf
<%IF true%>fasdf<%/IF%>
<%IF false%>1234<%/IF%>

注:
1. 不用考虑 <% %> 与 IF ELSE /IF 之间是否有空格,当没有处理。
2. IF 后面的条件有可能换行(感觉正则表达式里面处理带换行的任意文本很麻烦啊)
3. 内容有可能多行。

先谢过各位了!
...全文
465 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
BestAns1 2008-11-16
  • 打赏
  • 举报
回复
测试都通过了!
万分感谢!
BestAns1 2008-11-15
  • 打赏
  • 举报
回复
[Quote=引用 20 楼 lxcnn 的回复:]
才注意到这句,那这样就可以了

C# codeMatch m = Regex.Match(test.Trim(), @"<%IF.*?%>(((?<o>)<%IF.*?%>|(?<-o>)<%/IF%>|(?:(?!<%/?IF.*?%>).))*)(?(o)(?!))<%/IF%>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
if(m.Success)
richTextBox2.Text += "匹配成功:\n"+m.Value + "\n";
els…
[/Quote]

您好,对于这个正则表达式,处理以下字符串会出问题:
<%IF%><%IF%><%/IF%><%IF%>

以上字符串很明显不符合规则,但是用您的正则表达式后,会返回匹配成功的 <%IF%><%IF%><%/IF%>。
-过客- 2008-11-15
  • 打赏
  • 举报
回复
哦,少了一个^,第一个IF还是要加个判断的

Match m = Regex.Match(test.Trim(), @"^<%IF((?!%>).)*%>(((?<o>)<%IF((?!%>).)*%>|(?<-o>)<%/IF%>|(?:(?!<%/?IF.*?%>).))*)(?(o)(?!))<%/IF%>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
if (m.Success)
richTextBox2.Text += "匹配成功:\n" + m.Value + "\n";
else
richTextBox2.Text += "匹配不成功";
BestAns1 2008-11-14
  • 打赏
  • 举报
回复
感觉 lxcnn 的 . (Singleline模式)好简单清晰,为什么还要用 [\s\S]?是不是有什么细微的区别?

还有,IF 后面的 %> 是两个符号,“非 %>”怎么表示呢?如果是“非 %”可以用 [^%],但是两个符号怎么办?
BestAns1 2008-11-14
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 saucer 的回复:]
我上面的代码是针对你举的例子来的,假设了必有一个内嵌的IF,也许是我看错了

也许该换个思路

IF。。。IF。。。/IF....

遍历字符串,看到一个IF,往Stack上push该IF的位置,看到一个/IF,就pop一个IF,如果你需要知道该IF-/IF配对的位置,你也可以另行记录

如果看到一个/IF,但stack上没东西,那么这个/IF就是违法的,出错

如果到字符串全部结束,Stack还有东西,就表明那些位置没有匹配的/IF,也出错
[/Quote]

谢谢指教!

是这样的,我的这个解析器的所有元素(if 或 loop 等)都是通过正则表达式来处理的,所以不希望加入额外的处理代码。我觉得这个用正则表达式处理是可行的,所以就这么设计了。可能性能差,但是代码简单。

如果用代码来判断完整的 IF 的话,应该方法很多的,栈就是一个。但是现在只想通过正则表达式来处理。
yueyebohe 2008-11-14
  • 打赏
  • 举报
回复
以前懂得只不过是正则的一些皮毛而已,看了楼上诸位大侠的讨论,受益匪浅啊,学习了~
-过客- 2008-11-14
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 wumingcn 的回复:]
字符串一定会是 <%IF 开头的。所以,如果开头不是 <%IF,就没有匹配即可。
[/Quote]

才注意到这句,那这样就可以了

Match m = Regex.Match(test.Trim(), @"<%IF.*?%>(((?<o>)<%IF.*?%>|(?<-o>)<%/IF%>|(?:(?!<%/?IF.*?%>).))*)(?(o)(?!))<%/IF%>", RegexOptions.Singleline | RegexOptions.IgnoreCase);
if(m.Success)
richTextBox2.Text += "匹配成功:\n"+m.Value + "\n";
else
richTextBox2.Text += "匹配不成功";
-过客- 2008-11-14
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 wumingcn 的回复:]
感觉 lxcnn 的 . (Singleline模式)好简单清晰,为什么还要用 [\s\S]?是不是有什么细微的区别?

还有,IF 后面的 %> 是两个符号,“非 %>”怎么表示呢?如果是“非 %”可以用 [^%],但是两个符号怎么办?
[/Quote]

那就这样吧
            string test = @" 
<%IF 10%3=1%>
<%IF
""hello""=""hello""%>
asdf
<%/IF%>
<%ELSE%>
12
34
<%/IF%>

abcde

<%IF test%>one test <%/IF%>

fghij

<%IF 3+3=5%>asfdsf
<%IF true%>fasdf <%/IF%>
<%IF false%>1234 <%/IF%>

klmno

<%IF test%>another test <%/IF%> ";

Match m = Regex.Match(test, @"(?<=^(?:(?!<%/?IF.*?%>).)*)(<%IF.*?%>(((?<o>)<%IF.*?%>|(?<-o>)<%/IF%>|(?:(?!<%/?IF.*?%>).))*)(?(o)(?!))<%/IF%>)", RegexOptions.Singleline | RegexOptions.IgnoreCase);
if(m.Success)
richTextBox2.Text += "匹配成功:\n"+m.Value + "\n";
else
richTextBox2.Text += "匹配不成功";


[\s\S]和.加Singleline参数实现的效果是一样的,要说区别嘛,在于习惯吧,在效率上也有细微的差别

这里直接用非贪婪模式就行了,没必要用“非 %>”这种方式

如果在某一处应用需要排除两个字符以上,通常是用(?<!Exp)或者(?!Exp)来实现的
saucer 2008-11-14
  • 打赏
  • 举报
回复
我上面的代码是针对你举的例子来的,假设了必有一个内嵌的IF,也许是我看错了

也许该换个思路

IF。。。IF。。。/IF....

遍历字符串,看到一个IF,往Stack上push该IF的位置,看到一个/IF,就pop一个IF,如果你需要知道该IF-/IF配对的位置,你也可以另行记录

如果看到一个/IF,但stack上没东西,那么这个/IF就是违法的,出错

如果到字符串全部结束,Stack还有东西,就表明那些位置没有匹配的/IF,也出错
BestAns1 2008-11-14
  • 打赏
  • 举报
回复
谢谢 lxcnn,不过还是有问题:
IF 后面的表达式中,会用到 %,所以,用 [^%] 就不妥了。例如 <%IF 10%3=1%>
字符串一定会是 <%IF 开头的。所以,如果开头不是 <%IF,就没有匹配即可。

代码不用那么麻烦的,只需要找出第一个匹配的IF串即可,返回结果只需要有两种:匹配的IF串,或者空。您不用写那么多的代码的,只需要写一行正则表达式即可,别的代码我自己套就行啦。

再次谢过!
BestAns1 2008-11-11
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 lxcnn 的回复:]
其实不存在第几个不完整的问题,我们来分析一下:
其实不存在第几个不完整的问题,我们来分析一下:
1、有没有ELSE不影响结果,所以ELSE的情况完全可以忽略
2、假设是第二个IF不完整,那最终的字符串可以简化为这种形式:IF...IF.../IF,这样看来,我们还是可以认为是第一个IF不完整,所以只要有一个IF不完整,那么就可以认为是第一个IF不完整

忘了问楼主的目的了,是验证格式,还是从字符串中提取,按楼主提供的信息看,应该是验证格式[/Quote]

您忽略了一种情况,那就是第二个 IF 不完整,但是不影响第一个 IF 的完整性的情况,格式如下:
IF ... /IF ... IF ...

我的目的是提取+验证,就是解析一篇自定义的文档,因为需要定位出错误的大概位置,所以需要先提取第一个,然后在剩余字符串中提取第二个,依次类推,从而处理完整篇文档。

所以,对于 IF ... /IF ... IF ... 这样的格式,我想先提取出第一个 IF ... /IF ,然后继续提取字符串,这样就能知道哪个 IF 是错误的了。

您的正则表达式我测试了一下,对于 IF ... /IF ... IF 这种情况会认为匹配失败,应该能提取出前面的 IF ... /IF 部分才对。

-----

有限状态机需要写一堆代码,虽然不复杂,但是很麻烦。可能效率要高些,不知道我的理解是不是有什么偏差?我做的东西类似于编译器或解析器,就是根据自己的规则处理一篇文档,不知道该用什么方式最合适?

再次谢过!
zlb789 2008-11-11
  • 打赏
  • 举报
回复
学习
-过客- 2008-11-11
  • 打赏
  • 举报
回复
该用什么方式,我的理解是需要综合考虑:
有哪几种可以实现的方式
各种实现方式对效率或结果的影响如何
哪种是自己已经掌握的或是容易掌握的

根据楼主的需求,大致写个了
                        string test = @" 
<%IF 3+2=5%>
<%IF
""hello""=""hello""%>
asdf
<%/IF%>
<%ELSE%>
12
34
<%/IF%>

abcde

<%IF test%>one test <%/IF%>

fghij

<%IF 3+3=5%>asfdsf
<%IF true%>fasdf <%/IF%>
<%IF false%>1234 <%/IF%>

klmno

<%IF test%>another test <%/IF%> ";

Regex reg = new Regex(@"^(?:(?!<%/?IF[^%]*%>)[\s\S])*(<%IF[^%]*%>(((?<o>)<%IF[^%]*%>|(?<-o>)<%/IF%>|(?:(?!<%/?IF[^%]*%>)[\s\S]))*)(?(o)(?!))<%/IF%>)", RegexOptions.IgnoreCase);
while (reg.IsMatch(test))
{
richTextBox2.Text += "此处格式正确,IF结构的内容为:\n" + reg.Match(test).Groups[1].Value + "\n\n";
test = reg.Replace(test, "");
}
if (!Regex.IsMatch(test, @"<%/?IF[^%]*%>", RegexOptions.IgnoreCase))
{
richTextBox2.Text += "没有错误!";
}
else
{
richTextBox2.Text += "有错误,含有错误的文本为:\n" + test.Trim();
}


不好意思,目前只能晚上上网,解答问题的时效性太差。。。
-过客- 2008-11-10
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 wumingcn 的回复:]
“第一个”IF 有完整匹配,主要对应“例二”来说,就是,第二、三个 IF 是完整的,但是第一个 IF 不完整,因此就认为 IF 不完整。是否完整取决于第一个 IF。也许后面有不完整的 IF,但是这里只考虑第一个 IF。 [/Quote]

其实不存在第几个不完整的问题,我们来分析一下:
1、有没有ELSE不影响结果,所以ELSE的情况完全可以忽略
2、假设是第二个IF不完整,那最终的字符串可以简化为这种形式:IF...IF.../IF,这样看来,我们还是可以认为是第一个IF不完整,所以只要有一个IF不完整,那么就可以认为是第一个IF不完整

忘了问楼主的目的了,是验证格式,还是从字符串中提取,按楼主提供的信息看,应该是验证格式
            string[] str = new string[]{@" 
<%IF 3+2=5%>
<%IF
""hello""=""hello""%>
asdf
<%/IF%>
<%ELSE%>
12
34
<%/IF%>",@"<%IF 3+3=5%>asfdsf
<%IF true%>fasdf <%/IF%>
<%IF false%>1234 <%/IF%>", "<%IF test%>test <%/IF%>"};
foreach (string s in str)
{
Match m = Regex.Match(s, @"(?<=^\s*)<%IF[^%]*%>(((?<o>)<%IF[^%]*%>|(?<-o>)<%/IF%>|(?:(?!<%/?IF[^%]*%>)[\s\S]))*)(?(o)(?!))<%/IF%>(?=\s*$)", RegexOptions.IgnoreCase);
if (m.Success)
{
richTextBox2.Text += "符合条件,其内容为:\n";
richTextBox2.Text += m.Value + "\n\n";
}
else
richTextBox2.Text += "不符合条件\n\n";
}


如果是进行提取,因为中间可能存在不完整的IF对,所以正则很难胜任这个任务,可以考虑下有限状态机
BestAns1 2008-11-10
  • 打赏
  • 举报
回复
to saucer:
不好意思,上贴是我白痴没看懂。。。

重新测试了您的代码,问题如下:
1. 您忽略了最简单的情况,<%IF test%>test<%/IF%> 会认为不完整。
2. 将例一的第一个 <%IF 后面的 %> 删掉,同样会认为是完整的。而事实上,已经不完整了。
BestAns1 2008-11-10
  • 打赏
  • 举报
回复
不好意思,前两天放假,现在才看到帖子。
先谢谢lxcnn。

完整的 IF 表示:
有 IF 就一定要有 /IF,但是可以没有 ELSE。
例一,有嵌套的 IF,但是每个 IF 都有对应的 /IF,所以是完整的。
例二,第一个 IF 没有找到对应的 /IF,所以不是完整的。

“第一个”IF 有完整匹配,主要对应“例二”来说,就是,第二、三个 IF 是完整的,但是第一个 IF 不完整,因此就认为 IF 不完整。是否完整取决于第一个 IF。也许后面有不完整的 IF,但是这里只考虑第一个 IF。

我先按照 lxcnn 的指导重新测试 saucer 的代码,再次谢过!
兔子-顾问 2008-11-08
  • 打赏
  • 举报
回复
mark
zlb789 2008-11-08
  • 打赏
  • 举报
回复
学习下
Larry316 2008-11-08
  • 打赏
  • 举报
回复
.....学习
-过客- 2008-11-08
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 wumingcn 的回复:]
to saucer:
书我也翻了很多了,例子也见了不少,就是完全符合我的要求的没见到。
修改例子也做过,但是很奇怪,平时修改都行,放到递归匹配的正则表达式里面就不行了……

不好意思,您的代码执行有错,是正则表达式的错误。
另外,符号 . 并不包括换行符,因此是不合适的。
[/Quote]

每个人的需求都会有差别,当然不可能指望哪一本书能满足所有的需求

saucer的代码执行有错是CSDN的问题,回帖时如果不会插入语法方式就会自动在<前面加个空格,去掉就可以了
//saucer的代码
Regex re = new Regex("<%IF(?>(<%IF(?<DEPTH>)(?<CONTAIN>)|<%/IF(?<-DEPTH>)|.))*(?(DEPTH)(?!))(?(CONTAIN)|(?!))<%/IF%>", RegexOptions.Singleline | RegexOptions.IgnoreCase);


. 确实不包括换行符,而RegexOptions.Singleline这个参数就是为了改变.的含义的,看来楼主对于正则在C#中的应用还不是很熟

我对于楼主的描述还不是太清楚,什么叫“果第一个 IF 有完整的匹配”,主要是什么是“完整的匹配”
<%IF true%>fasdf <%/IF%> 为什么是不完整的,和举例一的区别在哪

是说一定要有IF嵌套,还是一定要有ELSE?
//这个是一定要有ELSE的
            string s = @" 
<%IF 3+2=5%>
<%IF
""hello""=""hello""%>
asdf
<%/IF%>
<%ELSE%>
12
34
<%/IF%>

<%IF 3+3=5%>asfdsf
<%IF true%>fasdf <%/IF%>
<%IF false%>1234 <%/IF%> ";
MatchCollection mc = Regex.Matches(s, @"<%IF(((?<o>)<%IF|(?<-o>)<%/IF|(?<c>)<%ELSE|(?:(?!<%/?IF)[\s\S]))*)(?(o)(?!))(?(c)|(?!))<%/IF%>", RegexOptions.IgnoreCase);
foreach (Match m in mc)
{
richTextBox2.Text += m.Value + "\n";
}
/*输出
<%IF 3+2=5%>
<%IF
"hello"="hello"%>
asdf
<%/IF%>
<%ELSE%>
12
34
<%/IF%>
/*



PS:好久没见saucer了,估计现在在睡觉^o^
加载更多回复(3)

110,539

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧