查找不能有某个字符串的正则匹配
^((?!hede).)*$
^(?!.*hede).*$
^((?!hede).)*$ 解释:
表达式(?!hede).会往前查找,看看前面是不是没有“hede”字串,如果没有(是其它字符),那么.(点号)就会匹配这些其它字符。这种正则表达式的“查找”也叫做“zero-width-assertions”(零宽度断言),因为它不会捕获任何的字符,只是判断。
在上面的例子里,每个空字符都会检查其前面的字符串是否不是‘hede’,如果不是,这.(点号)就是匹配捕捉这个字符。表达式(?!hede).只执行一次,所以,我们将这个表达式用括号包裹成组(group),然后用*(星号)修饰——匹配0次或多次:((?!hede).)**。
你可以理解,正则表达式((?!hede).)*匹配字符串"ABhedeCD"的结果false,因为在e3位置,(?!hede)匹配不合格,它之前有"hede"字符串,也就是包含了指定的字符串。
在正则表达式里, ?! 是否定式向前查找,它帮我们解决了字符串“不包含”匹配的问题。
^(?!.*hede).*$ 解释
在这种情况下我们使用 ^(?!.*hede).*$ 正则表达式 我们将第一个.*移到了零宽度断言的里面。在匹配的时候首先匹配行首的位置,然后接下来是匹配行首后面的位置,要求此位置的后面不能是 .*hede 匹配的字符串,说白了要求此位置的后面不能是xxxxxxxxxxxxxxxxxxhede 类似的字符串,这样就排除了从行首开始后面含有hede的情况了。
延伸的问题:
问题:查找有多个<img>标签的文本, img 与src 之间不能emoji 出现的标签
方案1: 方案1 demo
# reg_pattern = r'<img(?!.*emoji).*?src="([^"!"]*?\.(gif|png|jpg|webp))!?(.*?)".*?>'
方案1 解释:
本来是想找到img src之间不能有emoji ,根据字符串不能出现某个字符串的正则写法
^(?!.*helloworld).*$
所以我写成了 <img(?!.emoji).?src 这样,本想着这样可以,但是问题是多个img标签都是在同一个字符串里面,结果变成了 img 到最后src 之间不能有emoji ,所以中间的所有img都匹配不上了! 而且我的写法 .?src 已经是非贪婪模式,还是因为这个(?!.emoji) 变成了贪婪模式
方案二: 方案二 demo
#reg_pattern = r'<img(?![^"]*emoji)[^"]+?src="([^"!"]*?\.(gif|png|jpg|webp))!?(.*?)".*?>'
方案2 解释:由方案一我想到,我的需求是(?!.emoji)不要贪婪模式,然后看了资料写成(?![^"]emoji)以为在(?!)再写条件就可以避免贪婪, 结果原来这种写法是不行的,真正起作用的是 [^"]+?src 要求src前面不能有" 而我们的字符串刚好是防止前面有class="emoji" 恰巧有" 所以看上去才貌似起了不贪婪的作用, 其实(?![^"]*emoji) 不起作用的
最终方案: 最后方案 demo
reg_pattern = r'<img((?!emoji).)*?src=\"([^\"!]*?\.(gif|png|jpg|webp))!?([^\"]*?)\"((?!emoji).)*?>'
解释:看上面 ^(?!.hede).$ 解释
总结:
利用正则排除某字符串的方式有两种:
- ^(?!.helloworld).$
- ^((?!helloworld).)*$
区别是:
第一种相当于贪婪的模式, 整个字符串不能出现
第二种可以作为非贪婪模式的补充,限定不能出现的范围
问题: 查找不能有字符串abc的匹配
正则表达式 | 字符串 | 结果 |
---|---|---|
r'a(?=bbb)' | abbb | a |
r'abbb' | abbb | abbb |
python 2.7 文档说明 https://docs.python.org/2/library/re.html
(?=...)
Matches if ... matches next, but doesn’t consume any of the string. This is called a ** lookahead assertion **. For example, Isaac (?=Asimov) will match 'Isaac ' only if it’s followed by 'Asimov'.
(?!...)
Matches if ... doesn’t match next. This is a negative lookahead assertion. For example, Isaac (?!Asimov) will match 'Isaac ' only if it’s not followed by 'Asimov'.
** (?=) 可以看出这种形式的作用是判断条件不计入匹配结果,仅仅是判断条件**
(?<=...) **Group references are not supported ** even if they match strings of some fixed length. Note that patterns which start with positive lookbehind assertions will not match at the beginning of the string being searched;
** 说明(?) 里匹配到的内容都不能使用组引用比如\1 \g<1>引用对应的内容**
同理re.sub, re.search的替换也同样适合这种原理
tmp = 'abbb'
s = re.sub(r'a(bbb)', 'accc', tmp) # 整个匹配规则替换
# 结果
accc
s = re.sub(r'a(?=bbb)', 'accc', tmp) # (?=)里面的内容只做判断作用,不作为匹配结果,所以需要匹配某个规则,又不需要整个规则替换,可以用这个模式
# 结果
acccbbb
s = re.sub(r'a(?=bbb)', 'accc\g<1>', tmp)
# 报错:invalid group reference 不是匹配结果,所以不能引用
s = re.sub(r'a(bbb)', 'accc\g<1>', tmp)
# 正常可以引用
# 结果
acccbbb
s = re.findall(r'a(?=bbb)', 'abbb')
# 结果 不是匹配结果,所以不能引用
['a']
继续深入(?=)用法
re.findall('a(?=bhc)d', 'abhcd')
所以这个表达式的意思是:
a后边的表达式必须是bhc, 后面是d
结果: []
为什么? 因为这个(?=bhc)只是针对a后面的匹配, 不是针对d前面的,而对d来说,前面其实是没有匹配表达式
SO, d前面必须是a, 而a后面又必须时bhc,所以永远匹配不到任何东西
应该改成这样:
re.findall('a(?=bhc)\w*d', 'abhcd')
结果: ['abhcd']
分组引用问题
s = re.sub(r'a(?P<hello>xyz)', 'xx\g<hello>', 'axyz') # 使用组名引用
s = re.sub(r'a(xyz)', 'hello \g<1>', 'axyz') # 使用序号引用
s = re.sub(r'a(xyz)', 'hello \1', 'axyz') # 乱码
使用组名或者序号\g<1>来引用是没问题的,但是\1 \2引用会出问题 , 查了下资料,直接用\1这种引用的话,如果后面也是数字比如\10就会引用到第10组,所以避免歧义,还是用\g<1>这种吧
(?=) (?!) 实例:
title = re.sub(r'span\s+style\s*=\s*".*?color:\s*(.*?)[;"].*?>(.*?)</span>', 'font color="\g<1>">\g<2></font>', title)
# \1 \2 不行 要用\g<1>
# .*?color 是非贪婪模式, .*是匹配所有字符,但是到color就不要匹配,因为非贪婪模式
# 去掉所有非span, strong 的标签
# eg : a = '<span><p><strong> hello world </p> </strong> </span>'
# 执行后 被去掉了<p></p> 结果是:<span><strong> hello world </strong> </span>
title = re.sub(r'</?((?!span|strong).)*?>', '', title)
# 去掉换行和空格, 这里的换行可能要加上\r\n
title = re.sub(r'\n| ', '', title)
title = re.sub(r'color="(.*?)"', color_formater, title)
title = re.sub(r'</?(span).*?>', '', title)
- 重点讲解
title = re.sub(r'</?((?!span|strong).)*?>', '', title)
文章上面也有解析相关知识
</?((?!span).)*?> 这里可以拆开几个步骤:
- (?!span) 只是针对</ 这个匹配,意思是</ 后面不需不能是span
- 对于((?!span).)的 . 其实是可以匹配任何东西的,但是前面第一步说了</必须不能是span, 这样实际上就限定了. 不能是任何东西,只能是非span
- 那么((?!span).) 其实是可以看成 (.)*? 普通的组匹配,只不过组里面不能是span
- (.)?> 实际上可以看成.?> 非贪婪模式 匹配最近的一个> 结束符
参考:http://www.cnblogs.com/deerchao/archive/2007/02/15/651411.html
正则匹配换行符
需求: 将占位符[photo_0] [photo_1] .. 以及他们后面的换行符,一起替换成图片<img>,所以就需要匹配到换行符, 而换行符可能时\r 、\n 、\r\n 同时存在。做的时候只考虑了\n导致一致匹配不上!
ms_content = re.sub(r'\[photo_(\d+)\](\r\n|\r|\n)?', img_replace_func, content)
def img_replace_func(m):
"""替换图片用到的函数re.sub
global upload_url_list
"""
try:
index = int(m.group(1))
url = cdn_essay_upload_img_list[index]
img_html = '' % url
return img_html
except Exception, e:
return ''
相关链接 http://www.cnblogs.com/wangqiguo/archive/2012/05/08/2486548.html
findall VS search VS match 的区别
# 查找所有img标签
match = re.findall(r'(<img.*?src=(".*?")(.*?)>)', full_content)
src=".?" 是典型的非贪婪模式, 匹配最近的" 也就是匹配src里面的内容
.?> 也是同样道理,匹配最近的> 也就是整个img 标签的结束
一开始想着用re.search来做, 但是查了以后,search 和 match 都是只匹配一个, 区别是match只从开头匹配, search可以再字符串任意位置匹配。 但是他们的特点是: 只匹配一次!
所以我要想找所有的img标签, 用search就不合适了。 search找出来的group(0, 1, 2)都是针对一个匹配里面的子匹配!而不是多个匹配!
这里要用findall, 返回所有列表