您的位置:首页 > 编程语言 > Python开发

Python——正则表达式(2)

2016-02-29 18:25 561 查看
本文译自官方文档:Regular Expression HOWTO

参考文章:Python——正则表达式(1)

全文下载 :Python正则表达式基础

======================================================================================

3.使用正则表达式

现在,我们已经学习了一些简单的正则表达式,但我们应该怎么在Python中使用它们呢?re模块提供了一个连接正则表达式引擎的接口,允许你将RE编译成对象并利用它们进行匹配。

------------------------------------------------------------------------------------------------------------------------------------------------------

3.1.编译正则表达式
正则表达式被编译成模式对象,该对象拥有很多方法来进行各种各样的操作,比如按照模式匹配查找或者执行字符串的替换。

>>> import re
>>> p = re.compile('ab*')
>>> p
re.compile('ab*')
re.compile()方法也可以接受一个可选flags参数,用于指定各种特殊功能和语法变更,我们将会在之后一一学习。现在我们看一个简单的例子:

>>> p = re.compile('ab*',re.IGNORECASE)
正则表达式作为一个字符串参数传递给re.compile()方法。由于正则表达式并不是Python的核心部分,也没有特殊的语法去表示它们,所以只能被作为字符串处理(有许多应用并不需要RE,所以没有必要把RE纳入Python的核心部分),相反,正则表达式re模块仅作为C的扩展模块嵌入的Python中,就像socket或者zlib模块。

把正则表达式作为字符串也让Python更加简洁,但是也有一个缺点,下边我们就来谈一谈。
------------------------------------------------------------------------------------------------------------------------------------------------------

3.2.麻烦的反斜杠
就像上文描述的,正则表达式使用反斜杠 \ 来使一些字符拥有特殊的意义(比如\s)或者去掉特殊字符的特殊意义(比如\*就是表示星号而没有特殊的意义),这会与Python字符串中实现相同功能的字符发生冲突。

假如我们要写一个RE来匹配LaTeX文件中的一个字符串“\section”,首先你要先在程序代码中写出将要匹配的字符串。接着,你需要在反斜杠以及其他元字符前面加上反斜杠以去掉它们的特殊含义,所以得到结果“\\section”,这个字符串将传递给re.compile()函数。然而,要知道Python字符串中的反斜杠也有特殊意义,所以要再次在两个反斜杠前面加上反斜杠,得到字符串“\\\\section”。

匹配字符串
匹配步骤
\section将要匹配的字符串
\\section正则表达式中用‘\\’表示‘\’
“\\\\section”Python字符串中也用‘\\’表示‘\’
总之,要匹配一个反斜杠字符‘\’,你需要写四个反斜杠‘\\\\’作为一个正则表达式字符串,因为正则表达式必须为双斜杠 \\ ,而每个反斜杠在Python字符串中也要用双斜杠‘\\’表示。这就造成了我们需要重复很多次反斜杠,也让最后的正则表达式字符串难于理解。

解决方法是使用Python中的原始字符串。所谓原始字符串,即在字符串最前面加上字母r,这样字符串中的反斜杠都会被去掉特殊语义,看做普通字符。比如,字符串 r”\n” 是包含‘\’和‘n’两个字符的字符串,而字符串 “\n” 是只有一个换行符的字符串。正则表达式通常使用Python中的原始字符串来表示。

正则表达式字符串
原始字符串
“ab*”r”ab*”
“\\\\section”r”\\section”
“\\w+\\s+\\1”r”\w+\s+\1”
------------------------------------------------------------------------------------------------------------------------------------------------------
3.3.执行匹配

将正则表达式编译之后会得到一个模式对象,那么你会用它做什么呢?模式对象包含许多方法和属性,我们在这里只介绍最常用的几个,你可以通过查看re模块的文档来查看完整的列表。

方法/属性
功能
match()判断一个正则表达式是否从开始处匹配一个字符串
search()扫描一个字符串,找到正则表达式匹配的第一个位置
findall()扫描一个字符串,找到匹配正则表达式的所有子字符串,并将它们以列表的形式返回
finditer()扫描一个字符串,找到匹配正则表达式的所有子字符串,并将它们以迭代器的形式返回
如果没有找到匹配的字符串,match()和search()方法会返回None。如果匹配成功,则会返回一个匹配对象,包含匹配的信息:起始位置和匹配的子字符串等等。

你可以在交互模式下使用re模块来学习这些内容。如果你能够使用tkinter,你可以看一下Tools/demo/redemo.py这个程序,这是随着Python发布的一个示例程序。它可以让你输入正则表达式和字符串,并输出两者是否匹配。当你测试一个复杂的正则表达式的时候,redemo.py是非常有用的。Phil Schwartz’s Kodos也是一个开发和测试正则表达式的一个很有用的交互式工具。

我们使用标准Python解释器来解释这些例子。首先,打开Python解释器,导入re模块,然后编译一个RE:

>>> import re
>>> p = re.compile('[a-z]+')
>>> p
re.compile('[a-z]+')
现在你可以利用正则表达式 [a-z]+ 来匹配各种字符串。但是一个空的字符串并不能被匹配,因为加号 + 表示重复1次以上,在这种情况下,match()方法将会返回None。另外,这个结果在解释器中不会输出,不过你可以明确地调用print()方法来输出这个结果。

>>> p.match('')
>>> print(p.match(''))
None
接着,让我们尝试一个它可以匹配的字符串,比如字符串“tempo”。这种情况下,match()方法将会返回一个匹配对象(match  object),为了之后使用这个对象,你应该把这个结果保存在一个变量中。

>>> m = p.match('tempo')
>>> m
<_sre.SRE_Match object; span=(0, 5), match='tempo'>


现在你可以利用匹配对象查询匹配字符串的信息。匹配对象实例也有一些方法和属性,这里列出最重要的几个:

方法/属性功能
group()返回匹配的字符串
start()返回字符串匹配的开始位置
end()返回字符串匹配的结束位置
span()返回一个元祖表示匹配位置,(开始,结束)
尝试以下这些例子,你可以很快理解这些方法:

>>> m.group()
'tempo'
>>> m.start(),m.end()
(0, 5)
>>> m.span()
(0, 5)
group()方法返回由RE.start()和RE.end()位置确定的子字符串。span()方法用一个元祖返回子字符串的开始位置和结束位置。但要注意的是,match()方法是判断正则表达式是否从开始处匹配字符串,所以start()方法总是返回0。然而,search()方法就不一样了,它扫描整个字符串,匹配的子字符串的开始位置不一定是0。

>>> print(p.match(':::message'))
None
>>> m = p.search(':::message')
>>> print(m)
<_sre.SRE_Match object; span=(3, 10), match='message'>
>>> m.group()
'message'
>>> m.span()
(3, 10)
在实际的程序中,最常用的写法是将匹配对象存储在一个变量中,然后检查它是否为None。就像下边这样:

p = re.compile(…)
m = p.match(‘string  goes  here’)
if m:
print(‘Match  found: ’ , m.group())
else:
print(‘No  match’)
有两个方法可以返回所有匹配的子字符串。findall()方法返回所有匹配字符串的列表:
>>> p = re.compile('\d+')
>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
['12', '11', '10']
findall()方法需要在它返回结果之前创建出整个列表。但是finditer()方法将匹配对象作为迭代器返回(译者注:迭代器的方式更节省内存,效率更高)。
>>> iterator = p.finditer('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
>>> iterator
<callable_iterator object at 0x036A2110>
>>> for match in iterator:
print(match.span())

(0, 2)
(22, 24)
(40, 42)
------------------------------------------------------------------------------------------------------------------------------------------------------
3.4.模块级别的函数

你并不需要去创建一个匹配对象来调用它的方法,re模块中也提供一些全局函数比如match()、search()、findadd()、sub()等等。这些函数的第一个参数是正则表达式字符串,其他参数跟模式对象同名的方法采用一样的参数,返回值也一样,都是返回None或者匹配对象。
>>> print(re.match(r'From\s+','Fromage amk'))
None
>>> print(re.match(r'From\S+','Fromage amk'))
<_sre.SRE_Match object; span=(0, 7), match='Fromage'>
>>> print(re.match(r'From\S+','Fromage amk').group())
Fromage
>>> re.match(r'From\s+','From amk Thu May 14 19:12:10 1998')
<_sre.SRE_Match object; span=(0, 5), match='From '>
其实,这些函数只是简单地为你创建了一个模式对象,并且可以调用其相关函数。另外,它将编译好的模式对象存放在缓存中,所以如果之后你使用了相同的正则表达式,就不用了再次创建模式了,可以实现快速调用。

那么你应该使用这些模块级别的函数呢?还是应该先编译得到自己模式对象再调用它的方法呢?如果你要在一个循环中使用正则表达式,提前编译它可以节省函数的调用。但在循环外部,由于内部缓冲机制,两者的效率不差上下。
------------------------------------------------------------------------------------------------------------------------------------------------------
3.5.编译标志

编译标志可以让你在一些方面改变正则表达式的工作方法。编译标志在re模块中有两个可用名称:全称和简写,比如IGNORECASE的简写是字母I(如果熟悉Perl语言的模式编写,你会知道Perl语言的简写和这个一样,比如re.VERBOSE和简写是re.X)。多个编译标志可以通过逻辑或连接起来,比如re.I | re.M 设置了I和M两个标志。

下表列出了一些可用的编译标志:

编译标志
含义
ASCII,A使得转义符号如\w,\b,\s和\d只匹配ASCII字符
DOTALL,S使得点号 . 可以匹配任何符号,包括换行符
IGNORECASE,I匹配不区分大小写
LOCALE,L支持当前的语言(区域)设置
MULTILINE,M多行匹配,会影响^和$
VERBOSE,X(for ‘extended’)启用详细的正则表达式
I

IGNORECASE


匹配不区分大小写;使得字符类和文本字符串在匹配字符的时候不区分大小写。例如,[A-Z]也会匹配小写字母,Spam会匹配Spam、spam和spAM。如果你不设置LOCALE标志,则不会考虑语言(区域)设置方法的问题。

L

LOCALE


使得\w、\W、\b和\B取决于当前的语言环境,而不是Unicode数据库。

区域设置是C语言库的一个功能,主要为了在编写程序的时候考虑语言的差异性。比如,如果你正在处理一个法文文本,你想要写 \w+ 来匹配单词,但是 \w 只匹配出现在字符类[a-zA-Z]中的单词,它不会匹配 'é' 或者 'ç’ ,如果你的系统被设置为法语语言环境,那么C语言函数将会认为 'é' 也是一个字母。当编译正则表达式的时候设置了LOCALE标志,\w 就可以识别法文了,但是它的速度相对要慢一点。

M

MULTILINE


(^和$我们还没有提到,它们将在后面讲解)

通常的,^只匹配字符串的开头,而$只匹配字符串的结尾,当这个标志设置的时候,元字符^将会匹配字符串中每一行的的行首,而类似的,元字符$将会匹配每一行的行尾。

S

DOTALL


使得点号‘.’匹配所有的字符,包括换行符。如果不设置这个标志,点号‘.’将匹配除了换行符之外的所有字符。

A

ASCII


使得\w、\w、\b、\B和\S值匹配ASCII字符,而不是Unicode字符。这个标志仅对Unicode模式有意义,并忽略字节模式。

X

VERBOSE


这个标志可以让你组织正则表达式更具灵活性,从而写的正则表达式更具有可读性。如果设置了这个标志,正则表达式中的空格将会被忽略,当然这里不包括字符类中的空格,也不包括被反斜杠转义的空格,这会让你更清晰地去组织正则表达式。另外,这个标志也允许在正则表达式式中使用注释,井号 # 以及之后的字符将会被正则表达式忽略,除非井号 # 在字符类中或者经过了反斜杠转义。

下面看一个使用re.VERBOSE的例子:

>>> charref = re.compile(r'''
&[#]                   #开始数字引用
(
0[0-7]+         #八进制格式
|[0-9]+          #十进制格式
|x[0-9a-fA-F]+     #十六进制格式
)
;                      #结尾分号
''',re.VERBOSE)


如果不用VERBOSE设置,这个正则表达式将会是下面这个格式:

>>> charref = re.compile('&[#](0[0-7]+'
'|[0-9]+'
'|x[0-9a-fA-F]+);')
在上述的例子中,我们使用了Python自动串联字符串的功能,从而将正则表达式分成了几个更小的部分,但是它依旧没有使用re.VERBOSE版本的RE好理解。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: