正则表达式通用的学习地址:这里。
python里面re库的使用:这里。
这里写的都是正则表达式比较通用的,其它语言应该也没有问题
在pycharm中,写了一个正则表达式,可以把鼠标放在正则表达式上,按下Alt+Enter,然后可以在RexExp写上正则表达式的例子,看是否成功匹配,可以写很多,后面可以再进来查看示例的。
重要:==正则表达式里不要加多余为了美观或是习惯的 空格==
常用元字符 | 常用限定符 |
---|---|
. 匹配 除了换行符\n以外的任意字符 | * 重复零次或更多次 |
\w 匹配字母或数字或下划线或汉字(其它符号(好比!@#等)不行) | + 重复一次或更多次 |
\s 匹配任意的空白符(即空格,tab键) | ? 重复零次或一次 |
\d 匹配数字(后面不跟常用限定符,\d就代表匹配一个数字,其它同理) | {n} 重复n次 |
\b 匹配单词的开始或结束 (用的比较少,一般用下面这个) | {n,} 重复n次或更多次 |
^ & 匹配字符串的 ==开始== ==结束== | {n,m} 重复n到m次 |
\u4e00-\u9fa5 这是utf-8中,中文编码的范围,代表所有中文汉字 所以使用时记得加==[]==,代表一个中文汉字,即[\u4e00-\u9fa5] |
{n,m}这里面的逗号后不能跟空格 |
简单示例:
*
前边的内容可以连续重复使用任意次以使整个表达式得到匹配。因此.*
连在一起就意味着任意数量的不包含换行的字符;\d
匹配一位数字, 例如:匹配以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字,如028-12345678,那么表达式就是0\d{2}-\d{8}
# 如果这么写 0\d{2}-?\d{8} 即-后面多了一个?,就代表中间这个 - 可有也可以没有;ret = re.match(r"[0-9A-z_]{4,20}@163\.com", "songhui_cc@163.com")
python中的简单使用:正则表达式(Regular Expression):python菜鸟教程里有
这不仅仅局限于python,其它语言的正则应该也是可以的
[ ]
里的值只能选其中一个,也可以是r”[0-9 A-z]” (这就可以选0到9或所有大小写字母)(9和A之间可以没有空格);例如要找张三父母、李四父母这种(父母前面两个汉字字符),
那就是ptn = "[\u4e00-\u9fa5]{2}父母
[^0-9 A-z]
则是除去括号内所有元素后的任意字符,核心是前面加了一个^
符号,要跟[ ]
一起使用;如果是父母前面两个非汉字字符,
那就是ptn = "[^\u4e00-\u9fa5]{2}父母"
python中匹配到结果:要获取匹配的字符就是==re_out.group()==或者 re_out[0],没有匹配到,返回的就是None。
flags=re.M 与 flags=re.S 下面的讲解也有
import re
str = """three is a dog
with a bag"""
ptn = r"d[oac]g" # 这就是在 oac 三个字母中选择一个
print(re.search(ptn, str)) #结果是有的 (很重要,假设匹配的结果是 re_out, 要获取匹配的字符就是 re_out.group() 或者 re_out[0] ) 如果没数据,返回的是None
print(re.search(r"^w", str, flags=re.M)) # ^单用是匹配开头,给了flags=re.M参数才能匹配到同一字符串的第二行往后的开头
re.match(r".*", str, flags=re.S) # flags=re.S 就代表这个 . 现在可以代表 \n 了
除了以上写法,还有一种:
import re
pattern = re.compile(r"正则表达式")
pattern.match("匹配的内容")[0]
Tips:
match自动的默认是匹配开头所以开头的^可加可不加(所以开头都匹配不上的话是会直接算失败的),但是它不管结尾的,如下:
ret = re.match(r”\d{3}”, “123456”) # 它是成功的
但是我们如果要匹配一个变量是否符合规范,比如 name@123 这肯定是不行的,如果按照上面那么写,肯定是错的:
ret = re.match(r"[A-z_][A-z0-9_]*", "name@123")
if ret:
print("name@123 变量名符合规范", ) # 会打印这个,显然是错误的
else:
print("name@123 变量名根本不符合规范", )
修正:
ret = re.match(r"[A-z_][A-z0-9_]*$", "name@123") # 需要这样来匹配结尾,这就会返回None
正则解读:变量名以字母或下划线开头,第二个是任意个数字、字母或下划线,$代表要一直匹配到结尾,出现了@是不符个要求的,所以是None
所以像是邮箱这些,一定要加结尾的$,保证完整匹配
string = "_123@163.com"
ret = re.match("[A-z0-9_]{4,20}@163\.com$", string)
if ret:
print(f"{string}符合要求")
else:
print(f"{string}是错误的!")
Tips: 第二行如果不加==$==,那么"_123@163.comcom@qq"
这个都是可以的,它就只匹配到了前面那段,就不会管后面了。
match这一系列,在其它语言基本也是通用的,其它的可能是python的re库比较独有的,可能其它语言有,也可能没有。
re.match:默认匹配字符串的开始(相当于默认在正则里加了^),如果字符串开始不符合正则表达式,则匹配失败,函数返回None;
re.search:匹配整个字符串,直到找到一个匹配(找到一个后就不再往后去找了,这比较适合于去找某个确定的词是否存在)
re.findall:会一直找,找到所有匹配的值(这个比较适合模糊查找)
示例:
strings = "这是一个见得'美元债务'的测试,比如'新币债务'或者别的什么,'你好债务'吧"
word = r"[\u4e00-\u9fa5]{2}债务" # 前面是代表汉字
re_out = re.search(word, strings)
re_out = re.findall(word, strings)
解读:
[ ]
括起来(然后前面加不加r都可以);下面的正则表达式大多加了一对==``==,不是很方便去看的话,复制到别的编辑器里看。
re.sub(r”\d”, “998”, “python!=996,c++=1024”) # 将匹配到的字符串全部替换,然后返回替换后的值
sub示例:用正则表达式去掉一些不要的字符(==去掉乱码==),类似于replace:
new_str = re.sub(r'[^\u4e00-\u9fa5 0-9A-z ,。?]', '', old_str)
# 这就是只保留汉字、数字、字母和==,。?==3个符号,把非这里面的都替换为空(当然也可替换成其它的东西),别忘了前面的那个符号==^==sub的第二个参数还可以放函数调用 (这好像就是Python独有)
import re
def my_add(temp):
"temp:传的是一个正则匹配的结果"
strNum = temp.group()
new_num = int(strNum) + 1
return str(new_num)
strings = "python = 998, c++ = 1024"
# ret = re.sub(r"\d+", "1001", strings) # 这就是把所有匹配到的数字都换成1001,
ret = re.sub(r"\d+", my_add, strings) # 这就是把每个数字+1而已
分析:匹配到了几次,就回去调用my_add函数几次,一次就处理一个结果。
split:根据匹配进行切割字符串,并==返回一个列表==:
按照空格或冒号切割:ret = re.split(r”: | ”, “info:xiaonghao 33 hello”) |
注意,解读正则表达式 r”: | ” 其中| 是分枝条件或,左边是冒号,右边有一个空格 |
这里面的大抵都是正则表达式通用的。
格式:一个正则表达式 | 另一个正则表达式 # 从左往右测试每个条件,满足其一就不再管其他条件(注意| 两边不要有空格) |
例子:如果是想匹配 123abc@163.com 或者 bbbsa@qq.com 这两种邮箱格式,那么
ret = re.match(r"[0-9A-z_]{4,20}@(163|qq)\.com", "songhui_cc@163.com")
注意:写法是(163|qq)
必须用括号括起来,不然满足左边的条件,后面的.com就没了(再次注意不能有空格),然后因为.是特殊字符,所有还必须转义一下。
在正则中是将一个括号( )
中的字符作为一组,分组除了在上面的分枝条件中用到,还有一些特殊用法:
获取匹配的结果(想要知道被匹配到的那部分,就在那部分内容后面加个小括号==()==):
ret = re.match(r"([0-9A-z_]{4,20})@(163|qq)\.com", "songhui_cc@qq.com")
# @的前后都各有一个小括号,那么ret.group(1)得到的就是 songhui_cc ret.group(2)得到的就是qq
且后面的这个小括号不但帮助了分枝条件,还知道了内容的匹配\num:引用分组num匹配到的字符串(num就是数字1、2、3这些,不可能为0啊)
注意:这种都能匹配到,可如果标签错了,成了<h1>一对标签</h2>
还是能匹配到,
strings = r"<h1>一对标签</h1>" # <h1>一对标签</h2>
ret = re.match(r"<\w+>.*</\w+>", strings)
如果想要必须成对的标签是对的才行的话:==用小括号把\w+括起来成了 (\w+),后面的 \1
就代表第一个分组(也就是小括号)里匹配到的内容,那就保证了这对标签必须一致,那么<h1>一对标签</h2>这种就不会被匹配到==:
ret = re.match(r"<(\w+)>.*</\1>", strings)
同理,两对标签的话:
strings = r"<div><h1>一对标签</h1></div>"
ret = re.match(r"<(\w+)><(\w+)>.*</\2></\1>", strings) # 注意 \2 和 \1的顺序
分组起别名:(?P<name>)
引用分组别名:(?P=name)
: # 这里的name是自己随便起,其它的是固定写法
还是以上面的示例:
strings = r"<div><h1>一对标签</h1></div>"
ret = re.match(r"<(?P<my_lab1>\w+)><(?P<my_lab2>\w+)>.*</(?P=my_lab2)></(?P=my_lab1)>", strings)
还是注意顺序,然后这里面是(?P<my_label1>\w+)
相当于在匹配的字符(\w+)
前加了?P<my_lab1>
懒惰限定符就是在“常用限定符”后面加个==?==的组合, 代表重复的次数尽可能的少
例:aabab
用a.*b 匹配最长的以a开始,以b结束的字符串,,所以结果会是aabab;称为贪婪匹配
用a.*?b 匹配最短的,以a开始,以b结束的字符串,所以结果是 aab 和 ab
再来:+?
重复1次或更多次,但尽可能少重复
{n,}?
重复n次以上,但尽可能少重复 # 其它的类似
更多的还是看网页教程吧
零宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp。
例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。
同理,我们可以用(?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp
例如:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。
例子:从一段文字中提取国内手机号码
国内2017年底三大运营商现有号码开头[130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 145, 147, 150, 151, 152, 153, 155, 156, 157, 158, 159, 176, 177, 178, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189]
def main3():
str = """重要的事情说8140123456789遍,我的手机号是13512346789这个靓号,不是15600998765,也是110或119,王大锤的手机号才是15600998765。
是的14812345678是的,1512345687875"""
pattern = re.compile(r"(?<!\d)(1[3 8]\d{9}|14[5 7]\d{8}|15[0-3 5-9]\d{8}|17[6 7 8]\d{8})(?!\d)")
#注意上面这个表达式,把中间的分枝条件用一个括号括起来,再再在前面用前瞻和回顾
result = pattern.findall(str)
print(result)