需求:就是通过id获得一个html标签块。要处理的字符串:
<html>
<body>
<div id="div1">
<div id="div2" style="background:Red;">
<div id="div3">
<table id="table1">
<tr>
<td>
<div id="div4" style="width:100p
x"></div>
</td>
</tr>
</table>
</div>
</div>
<div id="div5">
<a href="http://www.csdn.net">csdn</a>
</div>
</div>
<img src="http://www.csdn.net/Images/logo_csdn.gif"/>
</body>
</html>输入输出样例1
输入:"div1"
输出:
<div id="div1">
<div id="div2" style="background:Red;">
<div id="div3">
<table id="table1">
<tr>
<td>
<div id="div4" style="width:100p
x"></div>
</td>
</tr>
</table>
</div>
</div>
<div id="div5">
<a href="http://www.csdn.net">csdn</a>
</div>
</div>输入输出样例2
输入:"div2"
输出:
<div id="div2" style="background:Red;">
<div id="div3">
<table id="table1">
<tr>
<td>
<div id="div4" style="width:100p
x"></div>
</td>
</tr>
</table>
</div>
</div>输入输出样例3
输入:"div3"
输出:
<div id="div3">
<table id="table1">
<tr>
<td>
<div id="div4" style="width:100p
x"></div>
</td>
</tr>
</table>
</div>
输入输出样例4
输入:"table1"
输出:
<table id="table1">
<tr>
<td>
<div id="div4" style="width:100p
x"></div>
</td>
</tr>
</table>调试代码:
目前只实现没有嵌套的匹配--"div4"
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string html = @"
<html>
<body>
<div id=""div1"">
<div id=""div2"" style=""background:Red;"">
<div id=""div3"">
<table id=""table1"">
<tr>
<td>
<div id=""div4"" style=""width:100px""></div>
</td>
</tr>
</table>
</div>
</div>
<div id=""div5"">
<a href=""http://www.csdn.net"">csdn</a>
</div>
</div>
<img src=""http://www.csdn.net/Images/logo_csdn.gif""/>
</body>
</html>";
Console.WriteLine(html);
string[] idList = { "div1", "div2", "div3", "div4", "table1" }; /* TODO : 这里发挥 */
string pattern = @"<([a-z]+)[^>]*\bid=(""|'){0}\2[^>]*></\1>"; foreach (string id in idList)
{
Match match = Regex.Match(html, string.Format(pattern, id),
RegexOptions.Singleline | RegexOptions.IgnoreCase);
Console.WriteLine("--------begin {0}--------", id);
if (match.Success)
Console.WriteLine(match.Value);
else
Console.WriteLine("o(╯□╰)o");
Console.WriteLine("--------end {0}--------", id);
}
Console.ReadKey();
}
}
}
<html>
<body>
<div id="div1">
<div id="div2" style="background:Red;">
<div id="div3">
<table id="table1">
<tr>
<td>
<div id="div4" style="width:100p
x"></div>
</td>
</tr>
</table>
</div>
</div>
<div id="div5">
<a href="http://www.csdn.net">csdn</a>
</div>
</div>
<img src="http://www.csdn.net/Images/logo_csdn.gif"/>
</body>
</html>输入输出样例1
输入:"div1"
输出:
<div id="div1">
<div id="div2" style="background:Red;">
<div id="div3">
<table id="table1">
<tr>
<td>
<div id="div4" style="width:100p
x"></div>
</td>
</tr>
</table>
</div>
</div>
<div id="div5">
<a href="http://www.csdn.net">csdn</a>
</div>
</div>输入输出样例2
输入:"div2"
输出:
<div id="div2" style="background:Red;">
<div id="div3">
<table id="table1">
<tr>
<td>
<div id="div4" style="width:100p
x"></div>
</td>
</tr>
</table>
</div>
</div>输入输出样例3
输入:"div3"
输出:
<div id="div3">
<table id="table1">
<tr>
<td>
<div id="div4" style="width:100p
x"></div>
</td>
</tr>
</table>
</div>
输入输出样例4
输入:"table1"
输出:
<table id="table1">
<tr>
<td>
<div id="div4" style="width:100p
x"></div>
</td>
</tr>
</table>调试代码:
目前只实现没有嵌套的匹配--"div4"
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string html = @"
<html>
<body>
<div id=""div1"">
<div id=""div2"" style=""background:Red;"">
<div id=""div3"">
<table id=""table1"">
<tr>
<td>
<div id=""div4"" style=""width:100px""></div>
</td>
</tr>
</table>
</div>
</div>
<div id=""div5"">
<a href=""http://www.csdn.net"">csdn</a>
</div>
</div>
<img src=""http://www.csdn.net/Images/logo_csdn.gif""/>
</body>
</html>";
Console.WriteLine(html);
string[] idList = { "div1", "div2", "div3", "div4", "table1" }; /* TODO : 这里发挥 */
string pattern = @"<([a-z]+)[^>]*\bid=(""|'){0}\2[^>]*></\1>"; foreach (string id in idList)
{
Match match = Regex.Match(html, string.Format(pattern, id),
RegexOptions.Singleline | RegexOptions.IgnoreCase);
Console.WriteLine("--------begin {0}--------", id);
if (match.Success)
Console.WriteLine(match.Value);
else
Console.WriteLine("o(╯□╰)o");
Console.WriteLine("--------end {0}--------", id);
}
Console.ReadKey();
}
}
}
不知道理解的对不对,以后还请大家多多指教!
document.getelementByid("asd").innerHTML不行吗或用jquery的 $(#fg).appen(..)
<img id="img1" src="images/ok.gif" />并且 id='img1' 或 id=img1 ,在浏览器中似乎都能正常工作,如果也要考虑这些情形,就更麻烦了。
说明
( 子表达式 )
捕获匹配的子表达式(或非捕获组;有关更多信息,请参见正则表达式选项中的 ExplicitCapture 选项)。使用 () 的捕获基于左括号按顺序从 1 开始自动编号。捕获元素编号为零的第一个捕获是由整个正则表达式模式匹配的文本。
(?< name > 子表达式)
将匹配的子表达式捕获到一个组名称或编号名称中。用于 name 的字符串不得包含任何标点符号,并且不能以数字开头。可以使用单引号替代尖括号,例如 (?'name')。
(?< name1 - name2 > 子表达式)
(平衡组定义。) 删除先前定义的 name2 组的定义,并在 name1 组中存储先前定义的 name2 组和当前组之间的间隔。如果未定义 name2 组,则匹配将回溯。由于删除 name2 的最后一个定义会显示 name2 的先前定义,因此该构造允许将 name2 组的捕获堆栈用作计数器,用于跟踪嵌套构造(如括号)。在此构造中,name1 是可选的。可以使用单引号替代尖括号,例如 (?'name1-name2')。有关更多信息,请参见本主题中的示例。
(?: 子表达式)
(非捕获组。) 不捕获由子表达式匹配的子字符串。
(?imnsx-imnsx: 子表达式)
应用或禁用子表达式中指定的选项。例如,(?i-s: ) 将打开不区分大小写并禁用单行模式。有关更多信息,请参见正则表达式选项。
(?= 子表达式)
(零宽度正预测先行断言。) 仅当子表达式在此位置的右侧匹配时才继续匹配。例如,\w+(?=\d) 与后跟数字的单词匹配,而不与该数字匹配。此构造不会回溯。
(?! 子表达式)
(零宽度负预测先行断言。) 仅当子表达式不在此位置的右侧匹配时才继续匹配。例如,\b(?!un)\w+\b 与不以 un 开头的单词匹配。
(?<= 子表达式)
(零宽度正回顾后发断言。) 仅当子表达式在此位置的左侧匹配时才继续匹配。例如,(?<=19)99 与跟在 19 后面的 99 的实例匹配。此构造不会回溯。
(?<! 子表达式)
(零宽度负回顾后发断言。) 仅当子表达式不在此位置的左侧匹配时才继续匹配。
(?> 子表达式)
(非回溯子表达式(也称为“贪婪”子表达式)。) 该子表达式仅完全匹配一次,然后就不会逐段参与回溯了。(也就是说,该子表达式仅与可由该子表达式单独匹配的字符串匹配。)默认情况下,如果匹配未成功,回溯会搜索其他可能的匹配。如果已知无法成功回溯,可以使用非回溯子表达式避免不必要的搜索,从而提高性能。
这类嵌套的问题应该说用平衡组来做是肯定的了,不过平衡组我也不熟,我觉得过客应该也可以解决
string html = @"
<html>
<body>
<div id=""div1"">
<div id=""div2"" style=""background:Red;"">
<div id=""div3"">
<table id=""table1"">
<tr>
<td>
<div id=""div4"" style=""width:100px""></div>
</td>
</tr>
</table>
</div>
</div>
<div id=div5>
<a href=""http://www.csdn.net"">csdn</a>
</div>
</div>
<img src=""http://www.csdn.net/Images/logo_csdn.gif""/>
</body>
</html>";
Console.WriteLine(html);
string[] idList = { "div1", "div2", "div3", "div4", "table1", "div5", "abc(def" }; /* TODO : 这里发挥 */
string pattern = @"<([a-z]+)(?:(?!id)[^<>])*id=([""']?){0}\2[^>]*>(?>(?<o><\1[^>]*>)|(?<-o></\1>)|(?:(?!</?\1).))*(?(o)(?!))</\1>"; foreach (string id in idList)
{
Match match = Regex.Match(html, string.Format(pattern, Regex.Escape(id)),
RegexOptions.Singleline | RegexOptions.IgnoreCase);
Console.WriteLine("--------begin {0}--------", id);
if (match.Success)
Console.WriteLine(match.Value);
else
Console.WriteLine("o(╯□╰)o");
Console.WriteLine("--------end {0}--------", id);
}
Console.ReadLine();
<div id=div5>
<a href="http://www.csdn.net">csdn</a>
</div>改进一:效率上
1、如果贪婪模式使用得法,会比非贪婪模式快很多,尤其是在数据源较大的情况下;而正则的效率瓶颈,以及效率陷阱,很多时候是由于非贪婪模式使用不当导致的
2、经测试((?!exp).)exp会比[^char]*?exp或[^char]*exp效率高,应该是.NET的NFA引擎做了优化,其它语言中不敢保证改进二:容错性
将id做了下预处理Regex.Escape(id)),否则输入的id为“abc(def”这类包含正则中需转义的字符时,会抛异常
Match match = Regex.Match(html, string.Format(pattern, Regex.Escape(id)),
RegexOptions.Singleline | RegexOptions.IgnoreCase);PS1:目前好像只有.NET支持平衡组和不定长的负向预搜索(?<=\d+)
PS2:固化分组(?>exp)好像也是绝大部分语言所不支持的
1、在频繁调用的情况下,考虑使用RegexOptions.Compiled参数,预编译
2、或者封装到assembly中
受教了,谢谢 lxcnn。
客客改进的正则很不错,建议把红色部分改为:
(?!\bid\b)否则对以下html串,因为width中含有id,会造成匹配不到:
<div style='width:100px' id='div4'></div>
建议把红色的部分改为:
""[^""]*""|'[^']*'|[^""'<>]否则对以下html串,因为onclick的引号中含有<,会造成匹配不到:
<div onclick='if(x<5){return true}' id='div4'></div>
还没用过Regex.Escape()方法,又学到了。感谢大家参与和完善实现细节。
建议在 = 的前后加上 \s*:
string pattern = @"<([a-z]+)(?:(?!\bid\b)""[^""]*""|'[^']*'|[^""'<>])*id\s*=\s*([""']?){0}\2[^>]*>(?>(?<o>)<\1[^>]*>|(?<-o>)</\1>|(?!</?\1).)*(?(o)(?!))</\1>";因为 <div id = 'div4'> 也是合乎规范的。
<div id='div4' onclick="if(x>5){print('</div>')}"></div>越来越复杂了:
string pattern0 = @"<([a-z]+)(?:(?!\bid\b)_)*id\s*=\s*([""']?){0}\2_*>(?>(?<o>)<\1_*>|(?<-o>)</\1>|(?!</?\1).)*(?(o)(?!))</\1>";
string pattern = pattern0.Replace("_", @"(?:""[^""]*""|'[^']*'|[^""'<>])");
另外,这个正则也无法匹配上面的 img1
感觉这个需求用jquery真好的。
<
([a-z]+) (?# 匹配标签名称,若整个表达式匹配成功,会自动编组为 1。但这里仍然有疏漏,无法匹配类似于 h3 之类的标签)
[^>]* (?# 匹配标签和 id 间的任意字符)
id=
([""']?) (?# 匹配双/单引号或空,注意,在正则中,空也可以看作一项匹配,单纯的空匹配总会成功。若整个表达式匹配成功,编组为 2。)
{0} (?# 这个值在楼主的程序中会被具体的 id 名称代替)
\2 (?# 反向引用分组 2)
[^>]*> (?# 匹配剩余的字符及尖括号)
(?> (?# 这是一个固化分组,表示本括号内后续的字符若匹配到了就不释放,以阻止回溯,可以提高效率)
(?<o>) (?# 空匹配,指定分组为 o。在这里的意思就是若发现后续的字符为分组 1 指定的标签,则压入一个位置进入堆栈,平衡组的开始)
<\1[^>]*> (?# 匹配分组 1 类型的标签)
|
(?<-o>) (?# 平衡组出栈。在这里就是若发现后续的字符为分组 1 指定标签的闭合标签,则弹出最后入栈的分组 o)
</\1> (?# 匹配分组 1 指定标签的闭合标签)
|
(?!</?\1). (?# 否定预查,匹配不属于分组 1 指定标签及其闭合标签中的任意字符,同时匹配标签和闭合标签是通过 /? 来达成的)
)* (?# 注意这个 * 号是针对整个括号的,所以它的作用范围是括号中的三个分支,这个括号的开头是一个固化分组)
(?(o)(?!)) (?# 这是一个条件测试,完整的形式形如 (?(group)(exp1)|(exp2)),或 group 匹配成功,则执行 exp1,否则执行 exp2)
(?# 因为 o 是一个平衡组,有入栈和出栈,若 o 全部出栈或根本未匹配到 o,则 (o) 为 false,正则引擎会继续匹配后续的表达式,即下面的 </\1>。若 o 未全部出栈,则 (o) 为 true,正则引擎会执行表达式 (?!),(?!) 总会导致匹配失败(因为空匹配总会成功,而对总会成功的匹配取反,则总会失败),从而导致正则引擎回溯,不去匹配最后的 </\1>。)
</\1> (?# 匹配分组 1 指定标签的闭合标签)呵呵,个人的理解,有不当之外,敬请指正。
另外,个人认为正则的匹配能刚好达到要求最好,一是可以避免太复杂的表达式,难以看懂,二是效率问题。