一个完整的正则使用过程
#调用re模块:
In [11]: import re
# re.match(pattern, string)第一个参数是你正则的规则, 第二个参数是检测的字符串:
In [12]: a=re.match(r"redhat","redhathello")
In [13]: print a.group()
redhat
#re.match尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回None:
In [14]: a=re.match(r"redhat","helloredhat")
In [16]: print a
None
# 如果没有找到匹配, 也返回None:
In [17]: a=re.match(r"redhat","hellolinux")
In [18]: print a
None
正则表达式(re=regular expression)
\d和\D
In [2]: a=re.match(r"\d","1234")
In [3]: print a.group()
1
In [5]: a=re.match(r"\D","he1234")
In [6]: print a.group()
h
\s和\S
In [9]: a=re.match(r"\s","\th123")
In [10]: print a.group()
#这里不是什么都没有返回,其实是返回了/t,一个table键的长度的空行
In [11]: a=re.match(r"\S","\t")
In [12]: print a
None
\w和\W
In [17]: a=re.match(r"\w","_h1")
In [18]: print a.group()
_
In [19]: a=re.match(r"\W","*#_h1")
In [20]: print a.group()
*
[ ]
根据[ ]的特性,下面这些都可以是等价的:
[0123456789] = \d = [0-9]
[^0123456789] = \D = [^0-9]
[a-zA-Z0-9_] = \w
[^a-zA-Z0-9_] = \W
[\n\t\r] = \s
[^\n\t\r] = \S
*
#代表a字符出现0次或多次
#match方法结果实际是函数,所以如果返回一个函数也能说明匹配成功了,如下就是成功匹配字符a在" "字符串中出现0次或多次
In [67]: re.match(r"a*","hello")
Out[67]: <_sre.SRE_Match at 0xfe6098>
In [68]: re.match(r"a*"," ")
Out[68]: <_sre.SRE_Match at 0xfca920>
+
#代表a字符至少出现1次
In [72]: print re.match(r"a+","hello")
None
In [75]: print re.match(r"a+","aahello")
<_sre.SRE_Match object at 0xfcad98>
练习1:匹配电话号码
需求:区号010-七位数字,中间“-”可以省略
#coding:utf-8
import re
reg=r"010-?\d{7}$"
#匹配010开头的,-可以出现1次或0次,单个数字出现7次并以这7位数字结尾
phones=["010-1234567", "0101234567", "010123","010-a123333"]
for i in phones:
a=re.match(reg,i)
if a:
print "%s 合法" %(a.group())
else:
print "%s 不合法" %(i)
#group方法只会打印出符和条件的内容,因此这里直接打印列表中的元素
执行结果:
练习2:匹配字符串
需求::匹配出字符串第一个字母为大写字母, 后面都是小写字母, 并且这些小写字母可有可无
#coding:utf-8
import re
s=["hello","Hello","hAlla","H","a"]
reg=r"[A-Z][a-z]*"
for i in s:
a=re.match(reg,i)
if a:
print "%s 合法" %(i)
else:
print "%s 不合法" %(i)
执行结果:
练习3:匹配qq邮箱
找出列表中符和条件的邮箱地址, 并存储到/tmp/mail.txt文件中;邮箱地址以@qq.com结尾;
@qq.com前面的内容由字母,数字或者下划线组成, 但至少4位, 最多20位
#coding:utf-8
import re
s=["dsadsad@qq.com","123@qq.com","qwer@qq.com","zsdcf@11.com"]
def ismail_ok(x):
reg=r"\w{4,20}@qq.com$"
a=re.match(reg,i)
if a:
return True
else:
return False
mail=[i+"\n" for i in s if ismail_ok(i)]
#使用列表生成式,如果符合条件就输出列表中的元素加上回车字符
with open("/tmp/mail.txt","a+") as f:
f.writelines(mail)
#由于mial列表中的元素已经自带换行了,所以这里直接用列表写入mail.txt文件中的每个元素是会自动换行的
执行结果:
|
In [5]: a=re.match(r"redhat|hello","hellohdjfhhhawreda")
In [6]: print a.group()
hello
In [9]: a=re.match(r"redhat|hello","redhathdjfhhhawreda")
In [10]: print a.group()
redhat
练习4:
匹配出0-100之间的数字, 包括1和100
#coding:utf-8
import re
s=["0","1","2","45","37","100","199","897","0.7","ab"]
reg=r"^0$|^100$|[1-9]\d?$"
#匹配0,匹配100,匹配以1-9开头任意数字结尾的
for i in s:
a=re.match(reg,i)
if a:
print "%s 合法" %(i)
else:
print "%s 不合法" %(i)
执行结果:
groups()方法
groups以元组方式返回符合条件的分组
In [25]: a=re.match(r"(\w{4,20})@(qq.com)$","abcd@qq.com") #这里多加了两对括号,表示元组里的内容
In [26]: a.groups()
Out[26]: ('abcd', 'qq.com')
In [31]: m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
In [32]: m.group(0)
Out[32]: '010-12345'
In [33]: m.group(1)
Out[33]: '010'
In [34]: m.group(2)
Out[34]: '12345'
In [35]: m.groups()
Out[35]: ('010', '12345')
匹配html语言:
普通方式(group方法)
In [35]: s="<html><h1>redhat</h1></html>"
In [36]: reg=r"<\w+><\w+>\w+</\w+></\w+>"
In [37]: a=re.match(reg,s)
In [38]: a.group()
Out[38]: '<html><h1>redhat</h1></html>'
元组方式(groups方法应用:返回关键字)
In [39]: s="<html><h1>redhat</h1></html>"
In [40]: reg=r"<(\w+)><(\w+)>(\w+)</\2></\1>"
#这里给每个匹配到的组加括号表示元组中的一个元素,后面的2和1可以代替前面已经匹配到的第二个\w和第一个\w
In [41]: a=re.match(reg,s)
In [42]: a.groups()
Out[42]: ('html', 'h1', 'redhat')
In [43]: a.group()
Out[43]: '<html><h1>redhat</h1></html>'
匹配网址:
元组方式(groups方法应用:返回网址中的jishu和book关键字)
In [44]: s="http://www.redhat.org/jishu/book"
In [45]: reg=r"http://.+/(\w+)/(\w+)$" #.表示匹配除了换行符"\n"之外的所有字符
#这里可以将想要匹配的关键字加括号,然后groups方法返回的元组中就会将这两个加括号的字符串作为元素
In [46]: a=re.match(reg,s)
In [47]: a.groups()
Out[47]: ('jishu', 'book')
In [48]: a.group()
Out[48]: 'http://www.redhat.org/jishu/book'
groupdict()方法
以字典方式返回符合条件的分组
In [52]: s="http://www.redhat.org/jishu/book"
In [53]: reg=r"http://.+/(?P<first_content>\w+)/(?P<second_content>\w+)$"
#以字典返回就必须加上一个key值,格式?P<>
In [54]: a=re.match(reg,s)
In [55]: a.groupdict()
Out[55]: {'first_content': 'jishu', 'second_content': 'book'}
In [56]: a.group()
Out[56]: 'http://www.redhat.org/jishu/book'
#可以看到无论是以元组返回关键字还是字典返回关键字都是不影响group方法正常使用的
In [57]: a.groups()
Out[57]: ('jishu', 'book')
In [5]: s="http://www.redhat.org/jishu/book/jishu/book"
In [6]: reg=r"http://.+/(?P<first_content>\w+)/(?P<second_content>\w+)/(?P=first_content)/(?P=second_content)$"
#如果有2个一样的关键字可以,可以在第二次匹配的时候直接调从第一次匹配到的字典里面取
In [7]: a=re.match(reg,s)
In [8]: a.groupdict()
Out[8]: {'first_content': 'jishu', 'second_content': 'book'}
In [9]: a.group()
Out[9]: 'http://www.redhat.org/jishu/book/jishu/book'
re高级用法
search()方法: 只找到符和条件的第一个并返回;
findall()方法: 返回符合条件的所有内容;
sub()方法: 对符合正则的内容进行替换;
split()方法: 指定多个分隔符进行分割;
search()方法
In [43]: s= "阅读次数为1000, 转发次数为100"
In [44]: a=re.search(r"\d+",s)
In [45]: print a
<_sre.SRE_Match object at 0x2289f38>
In [46]: print a.group()
1000
findall()方法
In [43]: s= "阅读次数为1000, 转发次数为100"
In [46]: b=re.findall(r"\d+",s)
In [47]: print b
['1000', '100']
sub()方法
In [43]: s= "阅读次数为1000, 转发次数为100"
In [49]: c=re.sub(r"\d+","0",s)
In [50]: print c阅读次数为0, 转发次数为0
题目5:
执行脚本之后阅读次数和转发次数都加1
解答:
#coding:utf-8import re
s="阅读次数为1000, 转发次数为100"def addNum(x):
a=int(x.group()) +1#re方法出来的是字符串,需要先转换成整形才能进行+1运算
return str(a)
reg=r"\d+"#匹配s中的数字,至少包含一个数字的字符串print re.sub(reg,addNum,s)
#在sub()方法中调用函数不用写(),在这里s就是传入addNum函数的实参
split()方法
In [55]: s="wangzhe 18:98766"
In [56]: print re.split(r":| ",s) #表示指定:和空格进行分隔
['wangzhe', '18', '98766']
python贪婪和非贪婪
默认情况下python正则是贪婪模式的
In [48]: s="this is a number 111-234-22-456"
In [49]: r=re.match(r".+(\d+-\d+-\d+-\d+)",s)
#.+表示匹配至少一个任意字符,这里的括号是groups方法的应用,可以查看前面groups的介绍
In [51]: r.group(1)
#查看第一个括号中匹配出的内容发现有两个1被.+匹配走了,只剩下了一个1,这是由于python默认的贪婪匹配导致的
Out[51]: '1-234-22-456'
非贪婪模式, 总是匹配尽可能少的字符
*, ?,+, {m,n}后面加上?, 使得贪婪模式变成非贪婪模式;
In [52]: s="this is a number 111-234-22-456"
In [53]: r=re.match(r".+?(\d+-\d+-\d+-\d+)",s)
In [54]: r.group(1)
Out[54]: '111-234-22-456'
JSON(JavaScript Object Notation)
是一种轻量级的数据交换格式。可以将字典转换为字符串,或者将字符串转换为字典
In [7]: dic={"service":"ftp","port":22}
In [8]: print json.dumps(dic)
{"port": 22, "service": "ftp"}
In [9]: print type(json.dumps(dic))
<type 'str'>
#coding:utf-8import json
dic={
"service":"ftp",
"port":22
}#将字典转换为字符串
in_json=json.dumps(dic,indent=4)#indent=4表示缩进为4,这样字符串就可以和上面字典的样子一样了print in_jsonprint type(in_json)#将字符串转换为字典
out_json=json.loads(in_json)print out_jsonprint type(out_json)
执行结果:
[root@python code12]# python json_test.py
{
"port": 22,
"service": "ftp"
}
<type 'str'>
{u'port': 22, u'service': u'ftp'}
<type 'dict'>
题目6:
获取ip地理位置
解答:
获取位置需要调用API,这里选择淘宝的API
#coding:utf-8import urllib2
#调用urllib2模块,可用于访问网址import json
ipadd=raw_input("输入要查询的IP:")
url="http://ip.taobao.com/service/getIpInfo.php?ip=%s" %(ipadd)#模拟浏览器访问指定链接
urlres=urllib2.urlopen(url)#读取访问的链接里面的内容
res=urlres.read()#print type(res) #可以看到这个API返回的内容是字符串,显然字符串很难处理,所以转换为字典就可以方便的处理了
res_dic=json.loads(res)#淘宝的API返回的是一个字典嵌套另一个字典,所以需要取两次keyprint "IP:%s" %(res_dic["data"]["ip"])print "Country:%s" %(res_dic["data"]["country"])print "Region:%s" %(res_dic["data"]["region"])print "City:%s" %(res_dic["data"]["city"])
执行结果:
[root@python code12]# python json_ip.py 输入要查询的IP:110.65.0.124
IP:110.65.0.124
Country:中国
Region:广东省
City:广州市
参考文档:http://blog.csdn.net/gf_lvah/article/details/79034371
总结
• re.match(p,text) :p 为正则表达式模式, text 要查找的字符串,会返回一个match 对象
• re.search(p,text) : 只要在 text 中匹配到了 p 就返回,只返回第一个匹配到的
• re.findall(p,text) :将能匹配上的全返回,会返回一个 list
• re.split(p,text) : 按照 p 匹配,并且以匹配到的字符为分隔符切割 text, 返回一个切割后的 list
• re.sub(p,s,text) : 替换,将 p 匹配到的字符替换为 s.
• pattern = re.compile(p) 先编译 p 模式,当正则表达式模式比较复杂的时候,会先编译,然后再使用
#coding:utf-8import re
mails=["aa.qq.com","redff@qq.com","21313@aa.com"]
reg = r"\w{4,20}@qq.com$"
reg=re.compile(reg) #事先将正则匹配编译好,再一个一个去匹配,提高脚本效率for i in mails:
a=re.match(reg,i)
if a:
print "%s 合法" %(i)
else:
print "%s 不合法" %(i)
执行结果:
[root@python code12]# python compile_test.py
aa.qq.com 不合法
redff@qq.com 合法
21313@aa.com 不合法
练习:
基础版:有一个日志文件access.log,统计访问前十的 IP 地址和访问次数升级版:有多个日志文件access.log,统计访问前十的 IP 地址和访问次数