Python笔记_22_正则表达式

正则表达式

  • 正则表达式是什么?
    它是约束字符串匹配某种形式的规则
  • 正则表达式有什么用?
    1.检测某个字符串是否符合规则.比如:判断手机号,身份证号是否合法
    2.提取网页字符串中想要的数据.比如:爬虫中,提取网站天气,信息,股票代码,星座运势等具体关键字
  • 在线测试工具 http://tool.chinaz.com/regex/

正则表达式所包含的元素种类

正则表达式由一些[普通字符]和一些[元字符]组成:

  • 普通字符包括大小写字母和数字
  • 元字符具有特殊含义,大体种类分为如下:
    • 预定义字符集,字符组
    • 量词
    • 边界符
    • 分组
匹配单个字符

[元字符] 预定义字符集

预定义字符集匹配内容
.匹配任意字符,除了换行符\n
\d匹配数字
\D匹配非数字
\w匹配字母或数字或下划线 (正则函数中,支持中文的匹配)
\W匹配非字母或数字或下划线
\s匹配任意的空白符
\S匹配任意非空白符
\n匹配一个换行符
\t匹配一个制表符
[]匹配中括号内列举的字符
字符组格式说明 [默认必须从字符组中选一个]
[…]匹配字符组中的字符
[^…]匹配除了字符组内所有内容,之外的所有字符
字符组内容待匹配字符匹配结果说明
[0123456789]8True字符组里枚举的各种字符,必须满足一个,否则返回假,不匹配
[abcdefg]9False由于字符组中没有"9"字符,所以不匹配
[0-9]7True可用 - 表示范围,[0-9] 和 [0123456789]是一个意思
[a-z]sTrue[a-z]匹配所有的小写字母
[A-Z]BTrue[A-Z]就表示所有的大写字母
[0-9a-fA-F]eTrue可以匹配数字,大小写形式的a-f. 该正则可验证十六进制
  • findall 匹配字符串中相应内容, 返回列表
    re.findall(正则表达式,要匹配的字符串)
import re
  • \d 匹配数字
    \D 匹配非数字
lst = re.findall("\d","sadfsdfsa 98&*^&^&92sdf3sdf839 sdf_as 神秘男孩 89")
print(lst)
lst = re.findall("\D","sadfsdfsa 98&*^&^&92sdf3sdf839 sdf_as 神秘卫星 89")
print(lst)
  • \w 字母,数字,下划线,包含中文(正则函数当中支持中文的匹配)
    \W 匹配非字母或数字或下划线
lst = re.findall("\w","09090sdf909sdf_+()()&*&*%^%^你好sd")
print(lst)
lst = re.findall("\W","09090sdf909sdf_+()()&*&*%^%^你好sd")
print(lst)
  • \s 匹配任意空白符
    \S 匹配任意非空白符
strvar = """1223
afdasdf
"""

lst = re.findall("\s","       			")
print(lst)
lst = re.findall("\s",strvar)
print(lst)
lst = re.findall("\S","   	lklkl sdfsdf sdfsdf")
print(lst)
  • \n 匹配换行
    \t 匹配一个制表符
strvar = """
今天	拉     肚										子
"""
lst = re.findall(r"\n",strvar)
print(lst)
lst = re.findall(r"\t",strvar)
print(lst)
字符组练习

必须从字符组选一个,如果一个都没有则匹配不成功

lst = re.findall("[123]","s3dafsf2s1")
print(lst)
print(re.findall('a[abc]b','aab abb acb adb'))


print(re.findall('a[0123456789]b','a1b a2b a3b acb ayb a9090909009b'))
# 优化版:0123456789 => 0-9 等价
print(re.findall('a[0-9]b','a1b a2b a3b acb ayb a9090909009b'))


print(re.findall('a[abcdefg]b','a1b a2b a3b acb ayb adb aabababbababb')) #['acb', 'adb']
# 优化版:a b c d .... z  => a-z
print(re.findall('a[a-g]b','a1b a2b a3b acb ayb adb aabababbababb')) 
print(re.findall('a[a-z]b','a1b a2b a3b acb ayb adb aabababbababb')) 


print(re.findall('a[ABCDEFG]b','a1b a2b a3b  aAb aDb aYb'))
# 优化版: A B C ... Z => A-Z
print(re.findall('a[A-G]b','a1b a2b a3b  aAb aDb aYb'))
print(re.findall('a[A-Z]b','a1b a2b a3b  aAb aDb aYb'))


print(re.findall('a[0-9a-zA-Z]b','a-b aab aAb aWb aqba1b')) 
# 优化版: a-zA-Z => A-z 可以匹配所有大小写,但是有缺陷,一些特殊符号也匹配到了
print(re.findall('[A-z]','('))
print(re.findall('[0-z]','9')) # 语法上允许,但是别这么用,没意义

print(re.findall('a[0-9][*#/]b','a1/b a2b a29b a56b a456bab'))
# ^ 字符组中的^ 代表除了的意思 
print(re.findall('a[^-+*/]b',"a%b ccaa*bda&bd"))

# 单独匹配一个特殊字符,用\让他的意思失效 转义字符
print(re.findall('a[\^]b',"a^b"))
匹配多个字符

[元字符] 量词符号

量词用法说明
?重复0次或1次
+重复1次或多次 (至少1次)
*重复0次或多次 (任意次)
{n}重复n次
{n,}重复n次或更多次 (至少n次)
{n,m}重复n到m次
.* .+贪婪模式匹配
.*? .+?非贪婪模式匹配
import re
'''1) ? 匹配0个或者1个a '''
print(re.findall('a?b','abbzab abb aab'))  

'''2) + 匹配1个或者多个a '''
print(re.findall('a+b','b ab aaaaaab abb'))

'''3) * 匹配0个或者多个a '''
print(re.findall('a*b','b ab aaaaaab abbbbbbb'))

'''4) {m,n} 匹配m个至n个a '''
print(re.findall('a{1,3}b','aaab ab aab abbb aaz aabb aaaaaaaaaabb'))
print(re.findall('a{1}b','aaab ab aab abbb aaz aabb aaaaaaaaaabb'))
print(re.findall('a{1,}b','aaab ab aab abbb aaz aabb aaaaaaaaaabb'))
贪婪匹配 与 非贪婪匹配

[语法:量词的后面加?号]

  • 贪婪匹配: 默认向更多次数匹配 底层用的是回溯算法;

  • 非贪婪匹配: 默认向更少次数匹配:

    在量词的后面加上一个?号,就是非贪婪匹配 例如: .*?   .??  .+?  .{m,n}?  .*?用的更多
    如果遇到了’子’这个字,在非贪婪模式下,匹配到第一个就返回.

    回溯算法:
    从左向右进行匹配,一直向后找,直到再也找不到了,回头,拿离右侧最近的那个值

strvar = "刘能和刘铁锤和刘大棍子12313子"
lst = re.findall("刘.",strvar)
# 贪婪匹配
print(lst)
lst = re.findall("刘.?",strvar)
print(lst)
lst = re.findall("刘.+",strvar)
print(lst)
lst = re.findall("刘.*",strvar)
print(lst)
lst = re.findall("刘.*子",strvar)
print(lst)
lst = re.findall("刘.{1,20}子",strvar)
print(lst,"<==>")

# 非贪婪匹配
strvar = "刘能和刘铁锤和刘大棍子12313子"
lst = re.findall("刘.??",strvar)
print(lst)
lst = re.findall("刘.+?",strvar)
print(lst)
lst = re.findall("刘.*?",strvar)
print(lst)
# 匹配到第一个子就直接返回
lst = re.findall("刘.*?子",strvar)
print(lst)

lst = re.findall("刘.{1,20}?子",strvar)
print(lst)
匹配开头结尾

[元字符] 边界符号

边界符说明
\b匹配一个字符的边界
^匹配字符串的开始
$匹配字符串的结尾
  • \b 用来匹配边界 word 匹配d为右边界 d\b 匹配w为左边界\bw
    \b 退格 backspace 是一个转义字符
    一般写正则表达式的时候,字符串的前面加上一个r,让转义字符失效,
# 贪婪匹配
lst = re.findall(r".*d\b","word pwd abc")
print(lst)
# 非贪婪匹配
lst = re.findall(r".*?d\b","word pwd abc")
print(lst)
# 优化版:舍掉空格
lst = re.findall(r"\S*?d\b","word pwd abc")
print(lst)
# 匹配单词的左边界
lst = re.findall(r"\bw.* ","word abc")

print(lst)
  • ^ 必须以某个字符开头,后面的字符无所谓
    $ 必须以某个字符结尾,前面的字符无所谓
    如果正则里面包含了^或者$ 意味着要把字符串看成一个整体
strvar = "大哥大嫂大爷"
print(re.findall('大.',strvar))
print(re.findall('^大.',strvar))
print(re.findall('大.$',strvar))
print(re.findall('^大.$',strvar))
print(re.findall('^大.*?$',strvar))

print(re.findall('^大.*?大$',strvar))
print(re.findall('^大.*?爷$',strvar))

# 把字符串看成一个整体,只要一个结果
print(re.findall('^g.*? ' , 'giveme 1gfive gay '))
print(re.findall('five$' , 'aassfive'))
print(re.findall('five$' , 'aassfive00'))
print(re.findall('^giveme$' , 'giveme'))
print(re.findall('^giveme$' , 'givemeq'))
print(re.findall('^giv.me$' , 'giveme'))

print(re.findall('^giveme$' , 'giveme giveme'))
print(re.findall('giveme' , 'giveme giveme'))
print(re.findall("^g.*e",'giveme 1gfive gay'))
匹配分组

[元字符] 分组符号

分组用法说明
a|b匹配字符a 或 字符b (如果两个当中有重合部分,把更长的那个放前面)
(ab)匹配括号内的表达式 ,将()作为一个分组
\num引用分组num匹配到的字符串
(?P)给分组命名
(?P=name)引用别名: 把name分组所匹配到的内容引用到这里
正常分组
  1. 正常情况下用()圆括号进行分组 可以用\1反向引用第一个圆括号匹配的内容。

  2. (?:正则表达式) 表示取消优先显示的功能

    (正则表达式) 代表分组 findall 把所有分组的内容显示到列表里
    (?:正则表达式) 代表分组 findall 把所有分组的内容不显示到列表里

import re

print(re.findall('.*?_good','wusir_good alex_good 神秘男孩_good'))
#['wusir_good', ' alex_good', ' 神秘男孩_good']
print(re.findall('(.*?)_good','wusir_good alex_good 神秘男孩_good'))
#['wusir', ' alex', ' secret男']
print(re.findall('(?:.*?)_good','wusir_good alex_good 神秘男孩_good'))
  • |代表或
    既想匹配abc 还要匹配abcd
lst = re.findall("abc|abcd","abc234abcd234")
print(lst) # 有缺陷,不能够都匹配到
#任意 把字符串长的写在前面,字符串短的写在后面
lst = re.findall("abcd|abc","abc234abcd234")
print(lst)
  • 匹配小数

\让原本有意义的字符变得无意义.
.代表任意字符(除了\n)
如果想要让正则匹配一个.
\.让点的意思失效.

# 3.14 34. .2342 234. . 2344.234
lst = re.findall("\d+\.\d+","3.14 34. .2342 234. . 2344.234")
print(lst)
  • 匹配小数和整数
lst = re.findall("\d+\.\d+|\d+","3.14 34. .2342 234. . 2344.234 343 sdfsdf ")
print(lst)
  • 使用分组的形式来匹配小数和整数
lst = re.findall("\d+(\.\d+)?","3.14 34. .2342 234. . 2344.234 343 sdfsdf ")
print(lst)
lst = re.findall("\d+(?:\.\d+)?","3.14 34. .2342 234. . 2344.234 343 sdfsdf ")
print(lst)
  • 匹配135或171的手机号
lst = re.findall("135|^171\d{8}","171skdjfksjkf 11312312317178787887")
print(lst)
lst = re.findall("135|171[0-9]{8}","171skdjfksjkf 17178787887")
print(lst)
  • 反向引用 拿已经匹配到的值,在引用一次
import re
strvar = "<h1>sdfsdfsdfsdf</h1>"
lst = re.findall("<(.*?)>(.*?)<(.*?)>",strvar)
print(lst)

strvar = "123<h1>sdfsdfsdfsdf</h1>  123<h2>ppoo</h2>"
lst = re.findall("<(.*?)>(.*?)<(.*?)>",strvar)
print(lst)

# 反向引用 拿已经匹配到的值,在引用一次 
# 引用第一个括号里面的内容 \1 
# 引用第二个括号里面的内容 \2 依次类推...
strvar = "123<h1>sdfsdfsdfsdf</h1>  123<h2>ppoo</h2>"
lst = re.findall(r"<(.*?)>(.*?)</\1>",strvar)
print(lst)
命名分组
  • (?P<组名>正则表达式)给这个组起一个名字
  • (?P=组名)引用之前组的名字,把该组名匹配到的内容放到当前位置
strvar = "d3j5sdj"

obj = re.search(r"(.*?)\d(.*?)\d(.*?)\1\2",strvar)
print(obj)
res = obj.group()
print(res)

# 用命名分组进行反向引用
obj = re.search(r"(?P<tag1>.*?)\d(?P<tag2>.*?)\d(?P<tag3>.*?)(?P=tag1)(?P=tag2)",strvar)
print(obj)
res = obj.group()
print(res)


obj = re.search(r"(?P<tag1>.*?)\d(?P<tag2>.*?)\d(?P<tag3>.*?)\1\2",strvar)
print(obj)
res = obj.group()
print(res)
search

通过正则匹配出第一个对象返回,通过group取出对象中的值

  • findall 把满足条件的所有值都找出来放到列表里面返回
  • search 找到一个满足条件的值就直接返回,扔到一个对象当中
    想要获取对象中的值,用group 对象.group()
obj = re.search("\d+","jsdfusudf8989sdfsdf89")
print(obj)
res = obj.group()
print(res)
  • 匹配 www.baidu.com 或者 www.oldboy.com
obj = re.search("(www)\.(baidu|oldboy)\.(com)","www.baidu.com www.oldboy.com")
res = obj.group()
print(res)
# 数字1 代表拿第一个小括号里面的内容
res = obj.group(1)
print(res)
# 数字2 代表拿第一个小括号里面的内容
res = obj.group(2)
print(res)
# 数字3 代表拿第一个小括号里面的内容
res = obj.group(3)
print(res)

# groups 一次性把所有小括号里面的内容匹配出来
res = obj.groups()
print(res)
  • “5*6-7/3” 匹配 5*6 或者 7/3
strvar = "5*6-7/3"
obj = re.search("\d+[*/]\d+",strvar)
res = obj.group()
print(res)

lst = re.findall("\d+[*/]\d+",strvar)
print(lst)

正则计算器-小项目

要求:用正则计算字符串:a = ‘1-2*((60 - 30+(-40/5)( 9- 25/3+7/399/42998+10568/14))-(-43)/(16-3*2))’ 的值

import re
# 计算乘除的方法
def parse_exp(exp):
	if "*" in exp:
		a,b = exp.split("*")
		# print(a,b)
		return str(float(a) * float(b))
		
	if "/" in exp:
		a,b = exp.split("/")
		return str(float(a) / float(b))

# 去除++ +- -- -+ bug情况
def exp_format(exp):
	exp = exp.replace("+-","-")
	exp = exp.replace("--","+")
	exp = exp.replace("-+","-")
	exp = exp.replace("++","+")
	return exp
	
# 实际计算
def exp_calc(strvar):
	# 计算乘除
	while True:
		res_obj = re.search("\d+(\.\d+)?[*/][+-]?\d+(\.\d+)?",strvar)
		if res_obj:
			res = res_obj.group()
			# print(res) #"5*-2"
			res2 = parse_exp(res)
			# print(res2)
			strvar = strvar.replace(res,res2)
		else:
			break
	# print(strvar)
	# 计算加减
	res = exp_format(strvar)
	# print(res)
	lst = re.findall("[+-]?\d+(?:\.\d+)?",res)
	# print(lst)
	count = 0
	for i in lst:
		count += float(i)
	# print(count)
	return count

# 去除括号
def remove_bracket(strvar):
	while True:
		res_obj = re.search("\([^()]+\)",strvar)
		if res_obj:
			res_exp = res_obj.group()
			# print(res_exp)
			# 计算括号里面的值,exp_calc
			res = str(exp_calc(res_exp))
			# print(res,type(res))
			# 把算好的实际数值转换成字符串,替换以前的圆括号
			strvar = strvar.replace(res_exp,res)
		else:
			# 直接返回替换好的字符串
			return strvar
# 主函数	
def main(strvar):	
	# 先把所有的空格去掉 
	strvar = strvar.replace(" ","")
	# 去除括号
	res = remove_bracket(strvar)
	# print(res)
	# 计算最后结果
	return exp_calc(res)
# strvar = "-  30  +(  40+5*-2)*    2"
a = '1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'

res = main(a)
print(res)
# 验证结果
res = eval(a)
print(res)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值