参考:re - Regular expression operations, Regular-Expressions in python
sys和os是python标准库中的两个模块,可以通过
>>> help(sys)
>>> help(os)
>>> dir(sys)
查看模块中的函数、变量等详细信息。
sys.argv[0] |
当前脚本或程序名称 |
sys.argv[i] (i>0) |
命令行参数 |
os.name |
当前操作系统 |
os.getcwd() |
当前工作目录 |
os.listdir() |
指定目录下的所有文件和目录名 |
os.remove() |
删除文件 |
os.system() |
运行shell命令 |
os.path.split() |
返回路径的目录名和文件名 |
os.path.isfile() |
检验路径是否为文件 |
os.path.isdir() |
检验路径是否为目录 |
os.path.exists() |
检验路径是否存在 |
[TOP]
首先来新建一个文件lesson1.txt:
>>> f = open('lesson1.txt', 'w')
>>> f.write('Good morning, class. \n')
>>> f.write('Godd morning, teacher.\n')
>>> f.write('Sit down, please.\n')
>>> f.write('My name is Han Meimei.')
>>> f.close()
这里的'w'
表示写模式,常用的还有r
读模式,a
追加模式,b
二进制模式,+
读/写模式。
注意:如果不使用换行符\n
,上面每次的输入会连接成一行。
读取文件:
>>> f = open('lesson1.txt', 'r')
>>> f.read(13)
'Good morning,'
>>> f.read()
' class.Good morning, teacher.Sit down, please.My name is Han Meimei.'
>>> f.close()
使用writelines()
写入多行文本:
>>> f = open('lesson1.txt', 'w')
>>> f.writelines(['Good morning, class.\n','Good morning, teacher.\n','Sit down, please.\n','My name is Han Meimei.'])
>>> f.close()
使用readlines()
读取全文:
>>> f = open('lesson1.txt', 'r')
>>> f.readlines()
['Good morning, class.\n', 'Good morning, teacher.\n', 'Sit down, please.\n', 'My name is Han Meimei.']
>>> f.close()
使用readline()
,每调用一次只读取一行:
>>> f = open('hi.txt', 'r')
>>> f.readline()
'Good morning, class.\n'
>>> f.readline()
'Good morning, teacher.\n'
>>> f.close()
>>> for line in open(lesson1.txt):
... process(line)
这种方式不需要再使用.close()
关闭文件。标准输入也可以采用类似的方法:
>>> import sys
>>> for line in sys.stdin:
... process(line)
[TOP]
>>> s = raw_input("Enter a string: ")
Enter a string: Good morning, class.
>>> s
'Good morning, class.'
>>> repr(s) #创建一个字符串,以合法的python表达式的形式来表示值
"'Good morning, class.'"
>>> len(s) #返回字符串长度
20
>>> s.split(',') #将字符串分隔,返回一个列表
['Good morning', ' class.']
>>> 'o' in s #判断s中是否包含'o',返回布尔值
True
>>> s.index('o') #从前向后查找指定字符串,返回第一个匹配项的位置
1
>>> s.find('o') #从前向后查找指定字符串,返回第一个匹配项的位置
1
>>> s.rfind('o') #从后向前查找指定字符串,返回第一个匹配项的位置
6
>>> s.count('o') #检索s中含有几个匹配项,返回匹配项数目
3
>>> s.replace(',', '-') #字符串的替换
'Good morning- class.'
>>> s.replace(',', '') #可以替换为空字符,即删除指定字符串
'Good morning class.'
>>> s.lower() #全部改成小写
'good morning, class.'
>>> s.upper() #全部改成大写
'GOOD MORNING, CLASS.'
>>> pieces = [x.strip() for x in s.split(',')] #忽略分隔后字符串首尾的空白字符
>>> pieces
['Good morning', 'class.']
>>> ','.join(pieces) #使用指定字符串连接列表中的元素
'Good morning,class.'
>>> n = '1024'
>>> int(n) #字符串转为整型
1024
>>> float(n) #字符串转为浮点型
1024.0
这些内置的字符串方法可以完成大部分的常用操作,更复杂的字符串处理需要借助于正则表达式模块re
。
[TOP]
python的正则表达式模块为re
,与shell的正则相比,re
更灵活,使用更方便,但语法也更复杂。
首先来学习一个简单的函数:
>>> re.search('o', s)
<_sre.SRE_Match object at 0x7f45631b03d8>
这个表达式表示从字符串s中查找与’o’匹配的项,如果匹配成功,返回一个MatchObject,否则返回一个NoneType。
为了更直观地看到匹配是否成功,可以这样处理:
>>> if re.search('o', s):
... print "Found 'o' in s."
Found 'o' in s.
>>> re.search('o', s) != None
True
有时候,字符串转义会给正则匹配带来不小的麻烦,python提供了raw
前缀标记“原生字符串”用于避免此类问题:
>>> re.search(r'o', s) != None #注意r
True
python2默认未采用unicode,因此最好使用u
标记,即ur'o'
,python3默认采用了unicode,只需使用r
标记。
注意:python可能会提示脚本中出现的utf-8字符无法识别,此时应在.py文件首部加上:
# encoding = utf-8
[TOP]
[ ]
: 字符集,匹配括号内出现的任意单个字符[ABCD]
匹配A、B、C、D任一字母;[A-Z]
匹配任意一个大写字母;[^A-D]
匹配一个不是大写字母的字符(^
在[ ]
内时表示取反)。
>>> re.search(r'^1[358][0-9]{9}', '13901390326') != None
True
这条命令可以匹配手机号码。
|
: 或,用于连接两个表达式>>> re.search(r'Han Meimei|Li Lei', 'My name is Han Meimei.') != None
True
>>> re.search(r'Han Meimei|Li lei', 'My name is Li Lei.') != None
True
>>> re.findall(r'My name is Han Meimei|Li Lei', 'My name is Han Meimei. My name is Li Lei')
['My name is Han Meimei', 'Li Lei']
函数findall()
返回一个列表,包含所有匹配项。
这个例子表明|
的有效范围是其两边的整个表达式,要避免这种情况,需要使用”无捕获组”,即:
>>> re.findall(r'My name is (?:Han Meimei|Li Lei)', 'My name is Han Meimei. My name is Li Lei')
['My name is Han Meimei', 'My name is Li Lei']
注意:[ ]
中的|
不表示或,而表示字符|
。
.
(一个点): 匹配除换行符之外的任意单个字符>>> re.findall(r'.+', 'hello\nworld')
['hello', 'world']
使用re.S(DOTALL)
可以使.
匹配包括换行在内的所有单个字符:
>>> re.findall(r'.+', 'hello\nworld', re.S)
['hello\nworld']
^
和$
: 匹配行首和行尾字符串python中与这两个元字符类似的还有转义字符\A
和\Z
,不同之处在于,\A
和\Z
并不会识别换行\n
,也就是匹配整个字符串的开头和结尾,而^
和$
则可以识别换行,可以匹配每一行的开头和结尾。
>>> re.findall(r'^wo', 'hello\nworld', re.M)
['wo']
>>> re.findall(r'\Awo', 'hello\nworld', re.M)
[ ]
类似\A
和\Z
的预定义转义字符还有匹配数字的\d
,补集为\D
;匹配字母和数字的\w
,补集为\W
;匹配间隔符的\s
,补集为\S
,\s
等价于[ \t\r\n\f\v]
(注意最前面有个空格);匹配单词边界的\b
等。
\b
: 匹配单词边界匹配单词的边界,可以看作是单词和间隔符、标点之间的位置,类似地,\B
匹配非单词边界。
>>> re.findall(r'ei', 'My name is Han Meimei.')
['ei', 'ei']
>>> re.findall(r'ei\b', 'My name is Han Meimei.')
['ei']
>>> re.findall(r'ei\B', 'My name is Han Meimei.')
['ei']
(?:)
: “无捕获组”对比这三个表达式:
>>> re.findall(r'My name is Han Meimei|Li Lei', 'My name is Han Meimei. My name is Li Lei')
['My name is Han Meimei', 'Li Lei']
>>> re.findall(r'My name is (Han Meimei|Li Lei)', 'My name is Han Meimei. My name is Li Lei')
['Han Meimei', 'Li Lei']
>>> re.findall(r'My name is (?:Han Meimei|Li Lei)', 'My name is Han Meimei. My name is Li Lei')
['My name is Han Meimei', 'My name is Li Lei']
可以发现如果要把一部分表达式作为一个整体操作,需要使用(?:re)
将其括起来,而不能只使用( )
,只使用( )
会构造出一个“组”,为了避免构造成组,需要使用“无捕获组”表达式(?:re)
。关于“组”的用法,请看这里。
(?#)
: 注释(?#comment)
中的’comment’在程序解析表达式时将被忽略。
*
表示匹配0次、1次或多次,+
表示匹配1次或多次,?
表示匹配0次或1次。
{n,m}
匹配至少n次,最多m次,{n}
精确匹配n次。
*
+
?
默认尽可能多的匹配字符,如提取c程序文件中的注释:
>>> s = r'/* comments1 */ code /* comments2 */'
>>> re.findall(r'/\*.*\*/', s)
['/* comments1 */ code /* comments2 */']
可以看到,该表达式把“code”也提取出来了,并不符合我们的本意,这时需要在*
后加上?
,表示“最小匹配”:
>>> re.findall(r'/\*.*?\*/', s)
['/* comments1 */', '/* comments2 */']
前向界定(?<=...)
中的...
用于指定匹配项前的字符,如:
>>> s = r'Han Meimei'
>>> re.findall(r'(?<=M)eim', s)
['eim']
后向界定(?=...)
中的...
用于指定匹配项后的字符,如:
>>> re.findall(r'Mei(?=m)', s)
['Mei']
上文提取c程序注释的表达式可以改写为:
>>> s = r'/* comments1 */ code /* comments2 */'
>>> re.findall(r'(?<=/\* ).*?(?= \*/)', s)
['comments1', 'comments2']
注意:(?<=...)
和(?=...)
中的...
不能包含正则式,必须是明确的字符串。
类似的,有前向非界定(?<!...)
和后向非界定(?!...)
,用于指定匹配项之前或之后不希望出现的字符。复杂的界定表达式可读性会变差,这时更适合使用“组”来处理。
>>> re.findall(r'My name is (Han Meimei|Li Lei)', 'My name is Han Meimei. My name is Li Lei')
['Han Meimei', 'Li Lei']
这个例子的findall()
只返回了( )
中匹配的内容,这就是“组”的基本功能。
组还可以命名(?P<name>…)
和引用(?P=name)
,如:
>>> s = r'Han Meimei'
>>> re.findall(r'(?P<p1>ei)m(?P=p1)', s) #提取中间为m的ei
['ei']
(?P<name>…)
中的...
可以是明确的字符,也可以是复杂的正则表达式。
还可以通过序号引用组表达式:
>>> re.findall(r'(ei)m\1', s)
['ei']
>>> re.findall(r'(ei)m(\1)', s)
[('ei', 'ei')]
[TOP]
re.match()
和re.search()
这两个函数的用法是类似的,不同之处在于re.match()
从字符串开头开始匹配,开头匹配失败则返回NoneType,而re.search()
则会检索整个字符串。
re.match()
和re.search()
包含三个参数,第一个是正则式,第二个是目标字符串,第三个是选项(修饰符),常用的修饰符有:
re.I(IGNORECASE) |
i |
忽略大小写 |
re.X(VERBOSE) |
x |
详细模式:忽略空白字符和注释 (注释以#开始,直到行尾) |
re.M(MULTILINE) |
m |
多行匹配,影响^ 和$ |
re.S(DOTALL) |
s |
使. 匹配包括换行在内的所有字符 |
re.U(UNICODE) |
u |
unicode模式 |
修饰符的使用方法:
>>> s = r'Han Meimei'
>>> re.search('m', s) != None
>>> re.search('(?i)m', s) != None
>>> re.search('m', s, re.IGNORECASE) != None
re.findall()
和re.finditer()
这两个函数也包含正则式、目标字符串和修饰符三个参数。
re.findall()
返回所有匹配成功的字符串,打印为一个列表,re.finditer()
则以迭代的方式返回匹配成功的字符串:
>>> s = r'Han Meimei'
>>> re.findall(r'\w', s)
['H', 'a', 'n', 'M', 'e', 'i', 'm', 'e', 'i']
>>> for i in re.finditer(r'\w', s):
... print i.group(), i.span()
H (0, 1)
... ...
i (9, 10)
re.sub()
和re.subn()
: 替换这两个函数用于将匹配的字符串替换为新的指定字符串,均包括正则式、新字符串、目标字符串、最多替换次数等参数。
re.sub()
返回处理后的字符串,re.subn()
返回一个元组,其中第一个元素是处理后的字符串,第二个元素是替换次数。
>>> re.subn(r'ei', r'a', s, 3)
('Han Mama', 2)
re.split()
: “切片”使用指定的正则式分隔目标字符串,包括正则式、目标字符串、切片次数三个参数,返回一个列表,如:
>>> s=r'Hello , World.'
>>> re.split('\s*,\s*', s)
['Hello', 'World.']
re.escape()
: 转义预处理可用于字符串的预处理,为目标字符串中的特殊字符加上转义符:
>>> s=r'Hello , \tWorld.'
>>> re.escape(s)
'Hello\\ \\,\\ \\\\tWorld\\.'
re.compile()
: 编译为pattern对象该函数包含正则式和修饰符两个参数,返回pattern对象,如:
>>> s1 = r'Good morning, class.'
>>> s2 = r'Good morning, teacher.'
>>> rc = re.compile(r'morning', re.I)
>>> rc.findall(s1)
['morning']
>>> rc.findall(s2)
['morning']
注意:这里的findall()
不只是函数了,而是pattern对象的方法。
[TOP]
pattern对象有很多方法,其中findall()
,finditer()
,match()
,search()
与同名函数的功能完全一样,不过其包含的参数有所不同,依次是目标字符串、指定起始位置、指定结束位置。如:
>>> s = r'Han Meimei'
>>> rc = re.compile(r'ei', re.I)
>>> rc.findall(s, 5, 10)
['ei', 'ei']
>>> rc.findall(s, 6, 10)
['ei']
pattern对象还有一些属性,如flags
返回一个整数,代指编译时的修饰符,pattern
返回编译时的正则式,groupindex
返回一个字典,包含正则式中的组及其序号(不包含无命名组)。
>>> rc.flags
2
match对象用于提取正则匹配结果MatchObject中的信息。
>>> s = r'Han Meimei'
>>> rc = re.compile(r'(?P<p1>mei)', re.I)
>>> m = rc.search(s, 3, 10)
>>> m.string #目标字符串
'Han Meimei'
>>> m.re.pattern #re生成pattern对象
'(?P<p1>mei)'
>>> m.group()
'Mei'
>>> m.group(1)
'Mei'
>>> m.group('p1')
'Mei'
此外,groups()
返回全部组,groupdict()
返回一个以组名为key、匹配结果为values的字典,start()
,end()
,span()
返回匹配成功的组的位置信息,pos
,endpos
返回正则方法输入的位置信息,lastindex
,lastgroup
返回最后匹配的组序号和组名。
最后,再介绍一个非常有用的expand()
,用于生成并返回一个新的字符串,如:
>>> s1 = r'HanMeimei 15 girl'
>>> s2 = r'LiLei 16 boy'
>>> rc = re.compile(r'(?P<name>[a-z]+)\s+(?P<age>[0-9]+)\s+(?P<gender>[a-z]+)', re.I)
>>> m1 = rc.search(s1)
>>> m2 = rc.search(s2)
>>> m1.expand(r'My name is \g<1>. I am a \g<gender> and I am \g<age>.')
'My name is HanMeimei. I am a girl and I am 15.'
>>> m2.expand(r'My name is \g<1>. I am a \g<gender> and I am \g<age>.')
'My name is LiLei. I am a boy and I am 16.'
[TOP]