(由于之前一段时间发生了一些事情,导致python学习缓停了很长时间,现在决定正式复出,哈哈哈,废话不多说,开始进行总结了)今天我总结的是正则表达式,以前我对正则表达式理解都很不正确,对其认识度不高,现在感觉正则表达式在一些字符处理,文件搜索上面作用非常大,我目前所能感受到的就是爬虫这个方面,我觉得一个爬虫的核心就是对网页代码处理那一块,而那一块最重要的就是正则表达式的编辑,一个好的正则表达式能够更快更准的达到我们所要的目的。接下来我就总结一下最近几天正则表达式的一些干货。

一:正则表达式概述:

正则表达式就是用某种模式去匹配一类有共同特征的字符串,最开始一般是调用regex模块,现在一般都是用re模块,re模块提供了Perl风格的正则表达式,具有更好的可读性,下面就是一些基本元字符

元字符是正则表达式中含有的字符,在正则表达式中可以在字符串中使用元字符用以匹配字符串的各种情况。

元字符还可以配合起来使用。“.*”可以匹配任意个字符,如“r.*d”会匹配“rd”,“red”,“read”等,“.+"可以匹配任意的一个或者多个字符,如“r.+d”会匹配“red”,"read",但是不会匹配“rd”,".?"可以匹配任意的零个或者一个字符,如“r.?d”会匹配“rd”,“red”,但是不会匹配“read”。“^”匹配行首,对于如下一段文字,“^red”只会匹配文中的第3个“red”,而对于“red$”则只会匹配文中的第二个“red”。

a red hat

blue and red

red and blue

在“[]”还可以用-来表示某一范围。例如[a-z]表示从a到z的所有小写字符,“[a-zA-Z0-9]”则表示任意的字母或者数字。

一般用来搜索时候可以用正则表达式来简化程序设计,比如

13[0-3][0-9]{8}这个就可以用来匹配联通手机号码,

13[4-9][0-9]{8}这个就可以用来匹配移动手机号码,

又例如匹配邮政编号可以这样来匹配,比如北京编码前三位是100则可以这样来编写正则表达式100[0-9]{3}.

一般来讲使用正则表达式匹配字符还是相对来说挺简单的,但是匹配一些字符串就相对来说要复杂一些了,需要考虑很多情况,例如需要找出某一文件中所有的网址则比较复杂。以“http://www.python.org”为例子,可以分为四部分,首先是“http://”,然后是“www”,再就是站名“python”,剩下的就是后缀“org”,一般有的网站书写可能完整,可能没有“http://”,那么可以将第一部分这样来写

(http://www|www) #使用“()”表示其为一个整体,使用“|”表示其中任何一个满足则匹配

中间的站名作为一部分,这部分可能为字母,数字或者“-”,因此该部分的正则表达式匹配可以写成如下所示的形式

[a-z0-9-]*   #[a-z0-9-]表示字母。数字或者“-”,“*”表示匹配0个或者多个前边的字符

剩下的后缀,考虑到可能为两个或者三个字符,因此可以写成如下形式

[a-z]{2,3}   #{2,3}表示匹配两次或者三次,则匹配由两个或者三个字母组成的字符串。

由于网站网址包含了“."特殊字符,则还需要将其使用“\”转义,完整的正则表达式如下所示

(http://www|www)\.[a-z0-9-]*\.[a-z]{2,3} #此处没有考虑诸如“com.cn”形式,各位可以自己尝试一下哦。

二:re模块函数的应用

1.匹配和搜索

 re.match(pattern,string[,flags])

re.search(pattern,string[,flags])

re.findall(pattern,string[,flags])

其中参数flags可以是如下选项

re.I:忽略大小写

re.L:根据本地设置而更改\w,\W,\b,\B,\s以及\S所匹配的内容。

re.M:多行匹配模式

re.S:使“.”匹配换行符

re.U:匹配Unicode字符

re.X:忽略pattern中的空格,并且可以使用“#”注释

上述的几个编译标志可以同时使用。同时使用几个编译标志需要用“|”对并用的编译标志进行运算。

2.替换函数

re.sub(pattern,repl,string[,count])

re.subn(pattern,repl,string[,count])

pattern:正则表达式模式

repl:要替换成的内容

string:进行内容替换的字符串

count:可选参数,最大替换次数。

subn返回一个元组,sub返回替换之后的字符串。

3.分割字符串函数

re.split(pattern,string[,maxsplit = 0])

pattern:正则表达式模式

string:要分割的字符串

maxsplit:可选参数,最大分割次数

三:正则表达式对象

1.以“\”开头的元字符

“\”开头的元字符也可以与其他的元字符配合使用。例如“\d*”可以匹配任何由数字组成的字符串,其中“\d”相当于[0-9],"\w"相当于[a-zA-Z0-9_]。例如下面的代码

import re

s = 'Python can run on Windows'

re.findall('\\bo.+?\\b',s) #查找首字母为o的单词

re.findall('\\Bo.+?\\b',s)#查找含字母o但不是首字母的单词

re.findall('\so.+?',s)#使用空白字符来匹配首字母为o的单词

re.findall('\\b\w.+?\\b',s)#查找字符串中的所有单词

re.findall('\d\.\d','Python 2.5')#查找x.x的数字形式

re.findall('\D+','Python 2.5')#查找不含数字的字符

re.split('\s',s)#使用空白字符分割字符串

re.split('\s',s,1)#使用空白字符分割字符串,但是只分割一次

re.findall('\d\w+?','abc3de')#查找以数字开始的字符

在上述例子中,使用“\\bo.+?\\b”来匹配首字母为o的单词而不是直接使用”\\b\o.+\\b“或者”\\b\o.*\\b“来匹配,如果直接匹配的话,那么由于”+“,”*“这种贪婪模式的元字符存在,会尽可能匹配更多的字符,那么最后结果会将on后面的空格和windows单词也匹配进来,为了避免这种情况,可以在其后面使用非贪婪模式的”?“或者直接使用”{}“控制次数。

2.编译正则表达式

re.compile(pattern[,flags])

下面举个实例来说明

import re

re.compile('a*b',re.I|re.X) #编译正则表达式,忽略大小写和模式中的空格

re.compile('''

...       \b     #匹配单词开始

...      A?    #以A或者AA开头

...      \d    #匹配一个数字

...     \w*   #匹配任意字符

...            #一个空行

...    \b     #匹配单词结束

...    ''',re.X)

三:使用原始字符串

原始字符串的设计是为了减少“\”在正则表达式中的数目

例如:

re.compile('\\ba.?')等同于re.compile(r'\ba.?')

re.compile('\\\\word')等同于re.compile(r'\\word')

四:正则表达式对象的属性和方法

1.匹配和搜索

search,match函数例子:

import re

r = re.compile('go*d')#编译正则表达式,“go*d”表示“g”和“d”之间有任意个以字母o开头的单词

r.match('Life can be good')#在字符串开始处匹配,没有返回值,表示匹配失败

r.match('Life can be good',12)#从字符串的第13个字符开始匹配(字符串从0开始),也就是从g开始,返回MatchObject对象实例

r.search('Life can be good')#在字符串中搜索“go*d”,返回MatchObject对象实例,表示字符串中含有“go*d”

r=re.compile('b.\sg')#重新编译,匹配字母’b‘和’g‘之间包含一个字母以及一个空字符的情况

r.search('Life can be good')#在字符串中搜索,此处匹配的是“be g”

r.compile('\w.\sg')#重新编译,匹配任意两个字母后跟一个空字符和字母’g‘的情况

r.search('Life can be good')#在字符串中搜索,此处匹配的是“be g”

r = re.compile('\\b\w..?\s')#匹配后边有一个空字符的任意包含两个或者三个字符的单词

r.findall('Life can be good')#使用findall()方法找找

2.替换

sub,subn函数例子

import re

s='''Life can be good;

...   Life can be bad;

...   Life is mostly cheerful;

...  But sometimes sad.'''

r = re.compile('b\w*',re.I) #编译正则表达式,忽略大小写

new = r.sub('*',s)   #使用sub()替换字符

new = r.sub('*',s,2)#使用sub()替换字符,不过只替换两次

r = re.compile('b\w*')  #重新编译,不忽略大小写

new = r.subn('*',s) #使用subn()替换字符,它返回一个元组

new = r.subn('*',s,1)#只在字符串中替换一次

3.分割字符串

split函数例子:

import re

s='''Life can be good;

...   Life can be bad;

...   Life is mostly cheerful;

...  But sometimes sad.'''

r = re.compile('\s')#编译匹配空字符的正则表达式

news = r.split(s)#以空字符分割字符串

news = r.split(s,4)#只分割4次

for new in news:   #遍历列表,输出分割后的字符串

... print new

...

Life

can

be

good;

Life can be bad;

Life is mostly cheerful;

But sometimes sad.

r = re.compile('b\w*',re.I) #编译匹配以字母b开头的字符串,忽略大小写

news = r.split(s)#分割字符串返回列表

news = r.split(s,1)#只分割一次

for new in news:

... print new

...

Life can

 good;

Life can be bad;

Life is mostly cheerful;

But sometimes sad.

r = re.compile('\w*e',re.I)#编译匹配以字母e结尾的字符串,忽略大小写

news = r.split(s)

五:使用组

组允许将正则表达式分解成几个不同的组成部分。在完成匹配或者搜索后,可以使用组编号访问不同部分匹配的内容。

1.组概述

在正则表达式中以一对圆括号"()"来表示位于其中的内容属于同一个组。例如“(re)+”将匹配“rere”,“rerere”等多个“re”重复的情况。组在匹配由不同部分组成的一个整体时常常有用。如电话号码由区号和号码组成,在正则表达式中可以使用两组来进行匹配:一组匹配区号,另一组匹配后面的号码。举个栗子:

>>>import re

>>>s = 'Phone No. 010-87654321'

>>>r = re.compile(r'(\d+)-(\d+)')

>>>m = r.search(s)

>>>m.group(1)

'010'

>>>m.group(2)

'87654321'

>>>m.groups()

('010','87654321')

在正则表达式中可以通过使用“(?P<组名>)”为组设置一个名字,通过使用如下所示,将第一个组的名字设置为“Area”,将第二个组的名字设置为“No”

r'(?P<Area>\d+)-(?P<No>\d+)'

那么可以将上面那个例子我们稍加修改一番,如下

>>>import re

>>>s = 'Phone No. 010-87654321'

>>>r = re.compile(r'(?P<Area>\d+)-(?P<No>\d+)')

>>>m = r.search(s)

>>>m.groupdict()

{'Area':'010','No':'87654321'}

>>>m.group('No')

'87654321'

>>>m.group('Area')

'010'

2.组的扩展语法

扩展语法如下所示

(?Lmsux):设置匹配标志,可以是i,L,m,s,u,x以及他们的组合。其含义与编译标志相同。

(?:...):表示此非一个组。

(?P=name):表示在此之前的名为name的值。

(?#...):表示注释

(?=...):用于正则表达式之后,表示如果“=”后的内容在字符串中出现则匹配,但是不返回=后的内容

(?!...):用于正则表达式之后,表示如果“!”后的内容在字符串中不出现则匹配,但不返回=后的内容

(?<=...):用于正则表达式之前,与(?=...)含义相同

(?<!...):用于正则表达式之前,与(?!...)含义相同

接下举一些例子:

import re

s='''Life can be good;

...   Life can be bad;

...   Life is mostly cheerful;

...  But sometimes sad.

... '''

r = re.compile(r'be(?=\sgood)')#编译正则表达式,只匹配气候单词为good的be

m=r.search(s)#搜索字符串

m.span()#输出匹配到的单词在字符串中的位置

(9,11)

r.findall(s) #使用findall()方法输出所有匹配的单词

r=re.compile('be')#重新编译,匹配be

r.findall(s)

r = re.compile(r'be(?!\sgood)')#匹配之后单词不为“good”的“be”

m = r.search(s)#搜索字符串

m.span()#

(27,29)

r=re.compile(r'(?:can\s)be(\sgood)')# 使用组来匹配“be good”

m = r.search(s)

m.groups()  #使用groups()方法输出组

(' good',)

m.group(1) #使用组编号输出组

r = re.compile(r'(?P<first>\w)(?P=first)')#使用组名重复,此处匹配具有两个重复字母的单词

r.findall(s)#输出匹配到的字母

r = re.compile(r'(?<=can\s)b\w*\b)')#匹配以字母“b”开头位于can之后的单词

r.findall(s)#输出匹配到的字母

r=re.compile(r'(?<!can\s)b\w*\b)')#匹配以字母b开头不位于can之后的单词

r.findall(s)

r = re.compile(r'(?<!can\s)(?i)b\w*\b') #重新编译,忽略大小写

r.findall(s) 

六:match对象

1.使用match对象处理组

import re

s='''Life can be dream,

...  Life can be great thoughts;

...  Life can mean a person,

...  Sitting in a count.'''

r=re.compile('\\b(?P<first>\w+)a(\w+)\\b')#编译正则表达式,匹配所有包含字母a的单词

m=r.search(s)#从头开始搜索,search()返回搜索的第一个单词

m.groupdict()#使用groupdict()输出字典

{'first':'c'}

m.groups()#使用groups()输出元组

('c':'n')

m=r.search(s,9)#从指定位置开始重新搜索

m.group() #输出匹配到的字符串

'dreams'

m.group(1)#输出第一对圆括号中的内容,即字母’a‘之前的部分

’dre‘

m.group(2)#输出第二对圆括号中的内容

’ms‘

m.group(1,2)#全部输出,返回一个元组

(’dre‘,’ms‘)

m.groupdict() #输出字典

{’first‘:'dre'}

m.groups() #使用groups()输出元组

('dre':'ms')

2.使用Match对象处理索引

start([groupid=0])

end([groupid=0])

span([groupid=0])

其中groupid为可选参数,即分组参数,如果不向其传递参数,则返回整个子字符串的索引。

import re

s='''Life can be dream,

...  Life can be great thoughts;

...  Life can mean a person,

...  Sitting in a count.'''

r = re.compile('\\b(?P<first>\w+)a(\w+)\\b')#编译正则表达式匹配含有字母a的单词

m = r.search(s,9)#从字符串中的第10个字符开始搜索

m.start()#输出匹配到子字符串的起始位置

12

m.start(1)#输出第一组的起始位置

12

m.start(2)#输出第二组的起始位置

16

m.end(1)#输出第一组的子字符串的起始位置

15

m.end()#输出子字符串的结束位置

18

m.span()#输出子字符串的开始和结束位置

(12,18)

m.span(2)#输出第二组子字符串的开始和结束位置

(16,18)

七:实例分析

下面是一个简单的使用正则表达式处理Python脚本中的函数和变量的例子。读者可以看一下

#使用正则表达式处理文件
import re
import sys
def DealWithFunc(s):
r = re.compile(r'''
(?<=def\s) #前面不许要有def且def后跟一个空格
\w+ #匹配函数名
\(.*?\) #后边必须跟一个“:”
(?=:) #设置编译选项,忽略模式中的选择

''',re.X)
return r.findall(s)
def DealWithVar(s):
vars = [] #定义一个列表,因为这里分两种情况处理
r = re.compile(r'''
\b #匹配单词开始
\w+ #匹配变量名
(?=\s=) #处理未为变量赋值的情况
''',re.X)
vars.extend(r.findall(s))
r=re.compile(r'''
(?<=for\s) #处理变量位于for语句中的情况
\w+ #匹配变量名
\s #匹配空格
(?=in) #匹配in
''',re.X) #设置编译选项,忽略模式中的注释
vars.extend(r.findall(s))
return vars()
#判断命令行是否有输入,没有则要求输入要处理的文件
if len(sys.argv) == 1:
sour = input('请输入要处理的文件路径')
else:
sour = sys.argv[1]
file = open(sour) #打开文件
s = file.readlines() #将文件内容以行读入s中
file.close() #关闭文件
print('*************************************')
print(sour,'中的函数有;')
print('************************************')
i = 0 #i为函数所在的行号
#循环处理每一行,匹配其中的变量,输出变量所在的行号,以及变量名
for line in s:
i = i + 1
function = DealWithFunc(line)
if len(function) == 1:
print('Line:',i,'\t',vars[0])
哇,今天写了半天,终于写完了,这篇就大致讲这些吧,溜了溜了。