一个比较复杂的HTML标签正则表达式匹配问题

有一段HTML如下:

    <div>
		巴拉巴拉<a>a标签包围的文本</a>度达<u>不</u><i>i标签包围的文本</i><u>u标签包围的文本</u>没有被内部标签包围的文本<u>度</u>要求。
	</div><br>
	<b>b标签包围的文本。</b><br>
	<p>不带格式的p标签包围的文本</p>
	<p style="text-indent:2em;">带首行缩进格式的p标签包围的文本</p>
	<div style="vertical-align:middle;">带居中对齐格式的div标签包围的文本</div>
	<div>
		<table border="1">
			<tr>
				<td rowspan="2">合并单元格</td>
				<td>第一行</td>
			</tr>
			<tr>
				<td>第二行</td>
			</tr>
		</table>
	</div>

现在想要保留其中的表格相关标签,img标签,br标签以及不带属性的div和p标签,但删除其他标签,以实现将该段内容变更为无格式内容但仍然保留文档中的换行、图片和表格的目标。

先考虑编写匹配排除某些标签(例如br、p、div)以外的html标签,这样才可能利用replace函数在将其他标签删除的同时保留br、p、div标签。且看下面的JavaScript代码:

let s = '<p style="text-indent:2em;">';
alert(/<\/?((?!br\b|div\b|p\b)\w)+[^>]*>/gi.test(s));

修改上面代码中的字符串s,可以看到只有标签名不是br、p和div时提示框才会显示true。

其中所使用的正则表达式具体含义如下:

1、br\b:\b为单词边界,匹配br标签的标签名。如果没有这个\b,自定义一个br开头的标签,例如<bre>,也会返回false。

2、(?!br\b)表示不匹配br标签的标签名。(?!br\b|div\b|p\b)分别表示不匹配br、div和p标签的标签名因为标签名前面或者是<或者是/,所以无需用\b限定。

3、形如(?!br\b)的正则表达式是0宽断言,所以为了可以匹配其他标签的标签名,后面加上了\w,即英文字母或数字。如果仅匹配字母本该写成[a-zA-Z],无所谓啦,\w短一点。

((?!br\b|div\b|p\b)\w)+

即表示匹配除了br、p和div标签以外的标签名。再在前面加上

<\/?

其实就是</?,反斜杠是转义字符,以区别JavaScript正则表达式的/符号与正斜杠。这样就可以匹配HTML元素标签的开始标签和结束标签。

4、[^>]*表示匹配任意多个(包括0个)不是“>”的字符,这样就既能匹配带属性的标签,也能匹配不带属性的标签。最后加上“>”,标签闭合了。

现在需要考虑如何删除带属性的p和div标签(br标签本就不带属性,无需再考虑)。参照第一段代码中的正则表达式,排除不带属性的p和div标签的正则表达式如下:

/<(div|p)>(((?!<\/\1>).)*)<\/\1>/gi

其中,<(div|p)>匹配<div>和<p>,即div和p元素的不带属性的开始标签。<\/\1>通过\1反向引用匹配开始标签的标签名,匹配与开始标签对应的结束标签。((?!<\/\1>).)*匹配的是开始标签与结束标签之间的内容,很显然,这部分内容的要求就是不能是结束标签(否则就不是开始标签与结束标签“之间”的内容了),这也正是((?!<\/\1>).)*的含义,其中的\1同样是反向引用匹配的标签名。((?!<\/\1>).)*外面再加上括号,是方便在替换操作时引用开始标签与结束标签之间的内容。

由于正则表达式有或操作符(|)却没有与操作符,其自身无法满足逻辑运算的需要,因此,要将本文开头的HTML片段中除表格相关标签,img标签,br标签以及不带属性的div和p标签以外的其他标签删除,只能考虑分两个步骤进行:

1、利用正则表达式:

/<\/?(((?!img\b|table\b|tr\b|td\b|br\b|div\b|p\b)\w)+[^>]*)>/gi

可以将其中标签名不是img、table、tr、td、br、div、p的所有HTML标签全部删除,但是带属性的p标签和div标签会被保留。

2、利用正则表达式:

/<(div|p)[^>]+>(((?!<\/\1>).)*)<\/\1>/gi

可以将带属性的div和p标签删除。

完整的测试JavaScript代码如下:

let html =
`<div>
	巴拉巴拉<a>a标签包围的文本</a>度达<u>不</u><i>i标签包围的文本</i><u>u标签包围的文本</u>没有被内部标签包围的文本<u>度</u>要求。
</div><br>
<b>b标签包围的文本。</b><br>
<p>不带格式的p标签包围的文本</p>
<p style="text-indent:2em;">带首行缩进格式的p标签包围的文本</p>
<div style="vertical-align:middle;">带居中对齐格式的div标签包围的文本</div>
<div>
	<table border="1">
		<tr>
			<td rowspan="2">合并单元格</td>
			<td>第一行</td>
		</tr>
		<tr>
			<td>第二行</td>
		</tr>
	</table>
</div>`


let reg = /<\/?(((?!img\b|table\b|tr\b|td\b|br\b|div\b|p\b)\w)+[^>]*)>/gi;
//替换掉img、table、tr、td、br、div、p以外的标签
html = html.replace(reg,'')
console.log(html);
//替换掉带属性的div和p标签
html = html.replace(/<(div|p)[^>]+>(((?!<\/\1>).)*)<\/\1>/gi,'$2');
console.log(html);

上述代码在浏览器中运行,两段控制台输出如下:

第一个console.log语句输出:

<div>
	巴拉巴拉a标签包围的文本度达不i标签包围的文本u标签包围的文本没有被内部标签包围的文本度要求。
</div><br>
b标签包围的文本。<br>
<p>不带格式的p标签包围的文本</p>
<p style="text-indent:2em;">带首行缩进格式的p标签包围的文本</p>
<div style="vertical-align:middle;">带居中对齐格式的div标签包围的文本</div>
<div>
	<table border="1">
		<tr>
			<td rowspan="2">合并单元格</td>
			<td>第一行</td>
		</tr>
		<tr>
			<td>第二行</td>
		</tr>
	</table>
</div>

第二个console.log语句输出:

<div>
	巴拉巴拉a标签包围的文本度达不i标签包围的文本u标签包围的文本没有被内部标签包围的文本度要求。
</div><br>
b标签包围的文本。<br>
<p>不带格式的p标签包围的文本</p>
带首行缩进格式的p标签包围的文本
带居中对齐格式的div标签包围的文本
<div>
	<table border="1">
		<tr>
			<td rowspan="2">合并单元格</td>
			<td>第一行</td>
		</tr>
		<tr>
			<td>第二行</td>
		</tr>
	</table>
</div>

可见第一步除带属性的div和p标签被保留外,其余想删除的标签都被删除了。第二步则将带属性的div标签和p标签也删除了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yivifu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值