title: 正则表达式re.sub替换不完整的问题现象及其根本原因

toc: true

comment: true

date: 2018-08-27 21:48:22

tags: ["Python", "正则表达式"]

category: ["Python"]

问题描述

问题的起因来自于一段正则替换。为了从一段HTML代码里面提取出正文,去掉所有的HTML标签和属性,可以写一个Python函数:

import re

def remove_tag(html):
text = re.sub('<.*?>', '', html, re.S)
return text

这段代码的使用了正则表达式的替换功能re.sub。这个函数的第一个参数表示需要被替换的内容的正则表达式,由于HTML标签都是使用尖括号包起来的,因此使用<.*?>就可以匹配所有<xxx yyy="zzz"></xxx>

第二个参数表示被匹配到的内容将要被替换成什么内容。由于我需要提取正文,那么只要把所有HTML标签都替换为空字符串即可。第三个参数就是需要被替换的文本,在这个例子中是HTML源代码段。

至于re.S,在4年前的一篇文章中我讲到了它的用法:Python正则表达式中的re.S

现在使用一段HTML代码来测试一下:

import re

def remove_tag(html):
text = re.sub('<.*?>', '', html, re.S)
return text source_1 = '''
<div class="content">今天的主角是<a href="xxx">kingname</a>,我们掌声欢迎!</div>
''' text = remove_tag(source_1)
print(text)

运行效果如下图所示,功能完全符合预期

再来测试一下代码中有换行符的情况:

import re

def remove_tag(html):
text = re.sub('<.*?>', '', html, re.S)
return text source_2 = '''
<div class="content">
今天的主角是
<a href="xxx">kingname</a>
,我们掌声欢迎!
</div>
'''
text = remove_tag(source_2)
print(text)

运行效果如下图所示,完全符合预期。

经过测试,在绝大多数情况下,能够从的HTML代码段中提取出正文。但也有例外。

例外情况

有一段HTML代码段比较长,内容如下:

<img></span><span>遇见kingname</span></a ><a  ><span class='url-icon'>< img '></span><span >温柔</span></a ><a  ><span >#青南#</span></a > <br />就在这里…<br />我的小侯爷呢???

运行效果如下图所示,最后两个HTML标签替换失败。

一开始我以为是HTML里面的空格或者引号引起的问题,于是我把HTML代码进行简化:

<img></span><span>遇见kingname</span></a><a><span><img></span><span>温柔</span></a><a><span>#青南#</span></a><br/>就在这里…<br/>我的小侯爷呢

问题依然存在,如下图所示。

而且更令人惊讶的是,如果把第一个标签<img>删了,那么替换结果里面就少了一个标签,如下图所示。

实际上,不仅仅是删除第一个标签,前面任意一个标签删了都可以减少结果里面的一个标签。如果删除前面两个或以上标签,那么结果就正常了。

答疑解惑

这个看起来很奇怪的问题,根本原因在re.sub的第4个参数。从函数原型可以看到:

def sub(pattern, repl, string, count=0, flags=0)

第四个参数是count表示替换个数,re.S如果要用,应该作为第五个参数。所以如果把remove_tag函数做一些修改,那么结果就正确了:

def remove_tag(html):
text = re.sub('<.*?>', '', html, flags=re.S)
return text

那么问题来了,把re.S放在count的位置,为什么代码没有报错?难道re.S是数字?实际上,如果打印一下就会发现,re.S确实可以作为数字:

>>> import re
>>> print(int(re.S))
16

现在回头数一数出问题的HTML代码,发现最后多出来的两个<br>标签,刚刚好是第17和18个标签,而由于count填写的re.S可以当做16来处理,那么Python就会把前16个标签替换为空字符串,从而留下最后两个。

至此问题的原因搞清楚了。

这个问题没有被及早发现,有以下几个原因:

  1. 被替换的HTML代码是代码段,大多数情况下HTML标签不足16个,所以问题被隐藏。
  2. re.S是一个对象,但也是数字,count接收的参数刚好也是数字。在很多编程语言里面,常量都会使用数字,然后用一个有意义的大写字母来表示。
  3. re.S 处理的情况是<div class="123" \n> 而不是<div class="123">\n</div>但测试的代码段标签都是第二种情况,所以在代码段里面实际上加不加re.S效果是一样的。

正则表达式re.sub替换不完整的问题现象及其根本原因的更多相关文章

  1. 在VS中用正则表达式查找或替换

    2005VS和2008VS正则替换规则如下(2013VS不适合,不需要的同学可以直接跳到2013VS的操作办法): Example: 查找#incldue中带有gl开头的头文件的,用include.+ ...

  2. xcode中使用正则表达式来搜索替换代码

    有这样的需求: 类似于 GLOBAL_STR(@"请继续添加"); 这样的代码,需要批量修改为: GLOBAL_STR(@"请继续添加", nil); 这里使用 ...

  3. notepad++使用正则表达式的查找替换

    使用正则表达式可以很好地完成很多繁琐耗时的工作,以下抄录editplus正则表达式的使用,同样适用于notepad++:表达式 说明\t 制表符.\n 新行.. 匹配任意字符.| 匹配表达式左边和右边 ...

  4. String replaceAll(String regex,String str)满足正则表达式的部分替换为给定内容

    package seday02;/*** * String replaceAll(String regex,String str)* @author xingsir*/public class Rep ...

  5. Java基础之一组有用的类——使用正则表达式查找和替换(SearchAndReplace)

    控制台程序. 使用正则表达式执行查找和替换操作,只需要调用Matcher对象的find()方法,就可以调用appendReplacement()方法来替换匹配的子序列.在提供给方法的新StringBu ...

  6. JAVA正则表达式匹配,替换,查找,切割(转)

    import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; public c ...

  7. VisualStudio2010正则表达式查找和替换

    把 TRACE(_T("something etc."); 替换为 TRACE(_T("something etc."));查找内容:表达式:TRACE\(_T ...

  8. C#正则表达式之字符替换

    string strTest= "www.BaiDu.com",strRst=""; //忽略大小写,将strTest中的BaiDu替换为baidu Regex ...

  9. Java SE之正则表达式三:替换

    /** * * @author Zen Johnny * @date 2018年4月29日 下午4:31:07 * */ package demo.regex; public class RegexR ...

随机推荐

  1. vue bus的使用

    vue中的bus事件,一般作为中央事件总线来使用 简单例子:比如在A,B组件为兄弟组件,现在A要调用B的中C事件 1.创建一个bus.js 内容: import Vue from 'vue' cons ...

  2. 解决ubuntu下firefox的flash全屏后不能恢复问题 ZT

    sudo mkdir /etc/adobe echo "OverrideGPUValidation=true" >~/mms.cfg sudo mv ~/mms.cfg /e ...

  3. echarts地图散点高亮弹框制作

    效果图如下: 实现大致思路: 引入echarts最新版本,还有china.json,以及地级市坐标,(因为产品需求中不考虑省份,只考虑各个地级市),echarts官网已经不让下载地图了,网上很多地级市 ...

  4. 重开ES6

    一.ES6的开发环境搭建 现在的Chrome浏览器已经支持ES6了,但是有些低版本的浏览器还是不支持ES6的语法,这就需要我们把ES6的语法自动的转变成ES5的语法. 1.建立工程目录: 先建立一个项 ...

  5. alpha冲刺(1/10)

    前言 队名:旅法师 作业链接 队长博客 燃尽图 会议 会议照片 会议内容 陈晓彬(组长) 今日进展: 召开会议 安排任务 博客撰写 构建之法的阅读 问题困扰: Java的学习感觉无从下手,学基础语法好 ...

  6. DRBD搭建

    基于块设备在不同的高可用服务器之间同步和镜像数据的软件,块设备可以是磁盘分区,LVM逻辑卷或整块磁盘,解决磁盘单点故障 三种复制协议 (1)协议A:异步复制协议,本地写成功后立即返回,数据放在发送bu ...

  7. xamarin C# 安卓实现 ListView 放大缩小

    翻译自java示例https://raw.githubusercontent.com/Xjasz/AndroidZoomableViewGroup/master/ZoomListView.java u ...

  8. PythonStudy——列表与字典推导式 List and dictionary derivation

    # 快速生成列表或字典的语法糖,且能在生成过程中添加简单的逻辑 # 能被列表推导式推导的数据源必须在循环取值时可以得到一个值 ls = [v for v in range(1, 6)] print(l ...

  9. GoStudy——解决vscode中golang插件依赖安装失败问题

    vscode中安装ms-vscode.go插件后可以开启对go语言的支持,ms-vscode.go插件需要依赖一些工具,安装完成后提示 Installing github.com/nsf/gocode ...

  10. js 函数问题

    replace() is not a function 1,情景描述:做项目的时候想把内容的某些字符给替换成另外一些字符 2,replace用法:replace() 方法用于在字符串中用一些字符替换另 ...