今天有这样一个需求:需要匹配好多个HTML文件,从中找出所有的标题文字。
正则表达式
这本是一个简单的需求,只需要使用正则表达式进行匹配即可。下列是我们当时所使用的表达式:
<[hH][1-6]>.*?<\/[hH][1-6]>
测试
我们编写了测试数据,并获取到测试结果:
这是我们的测试数据,获取到的测试结果为:
可以看到,这其实没有问题。但是,我们的同学在实操的过程中,出现了一些失误,就是:他不小心打开了HTML文件,导致操作出现了一些问题。简单来说就是:因为勿删或其它原因,使得HTML标签匹配不完整,该正则表达式将无效标签(如<h2></h3>
这样的)也匹配到了。
怎么解决呢?
反向引用
如,你想把连续重复复现两次的单词打印出来,就可以使用反向引用。
反向引用允许正则表达式模式引用之前匹配的结果。基本原理是:反向引用在捕获组匹配成功时,会将子表达式匹配到的内容,保存到内存中一个以数字编号的组里,可以简单的认为是对一个局部变量进行了赋值,这时就可以通过反向引用方式,引用这个局部变量的值。捕获组在匹配成功之前,它的内容可以是不确定的,一旦匹配成功,它的内容就确定了,反向引用的内容也就是确定的了
反向匹配的符号是:\n
,例如:\1
表示匹配模式中所使用的第一个子表达式,\2
匹配第二个子表达式,\3
匹配第三个,以此类推。
匹配重复字符
如:匹配这段文字repeated, and and they
里连续重复两次的单词,可以使用正则表达式:[ ](\w+)[ ]\1
。
注意,\w+
是出现在括号里,所以它是一个子表达式,该子表达式对模式进行分组,将其标识出来以备候用。模式最后一部分是\1
,这是对前面那个子表达式的反向引用,\1
匹配的内容与第一个分组匹配的内容一样。如果(\w+)
匹配的单词是of,那么\1
也匹配 of。
匹配HTML标签
这下,针对无效标签,就可以使用反向匹配来解决了。表达式为:
<([hH][1-6])>.*?<\/\1>
我们写了这样的一组测试数据:
let html = `<div><h1>将进酒</h1>This is the Qiangjinjiu full text:<br/><h2>君不见黄河之水天上来,奔流到海不复回。</h2>the next<h3>君不见高堂明镜悲白发,朝如青丝暮成雪。</h1><h2>人生得意须尽欢,莫使金樽空对月。</h3><h2>天生我材必有用,千金散尽还复来。</h3><h2>烹羊宰牛且为乐,会须一饮三百杯。</h2>岑夫子,丹丘生,将进酒,杯莫停。与君歌一曲,请君为我倾耳听。钟鼓馔玉不足贵,但愿长醉不愿醒。古来圣贤皆寂寞,惟有饮者留其名。陈王昔时宴平乐,斗酒十千恣欢谑。主人何为言少钱,径须沽取对君酌。五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。The End.</div>`let reg = /<([hH][1-6])>.*?<\/\1>/gconsole.log(html.match(reg))
结果是:
<h1>将进酒</h1>
<h2>君不见黄河之水天上来,奔流到海不复回。</h2>
<h2>烹羊宰牛且为乐,会须一饮三百杯。</h2>
我们还使用了多组测试数据,这种写法的表达式都不会出错,匹配HTML标签的问题完美解决。
总结
对于使用正则表达式匹配HTML标签,我从来没有想过会有这样的问题出现。本以为是非常简单的一件事情,在这个问题上还是处理了很久。好在我们最后发现了错误的原因和错误本身、并且找到了使用反向引用这样第一个写法,完美解决了问题。
在解决这个问题的过程中,反正引用真的特别重要,要是没有反向引用,我们可能根本没法解决问题或需要花费更大的力气来解决这个问题。