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

Python中的正则表达式(5)

2012-09-18 17:44 190 查看
修改字符串

到目前为止,我们已经简单实现了字符串的静态搜索;另外,正则表达式也常常用于通过不同的方式修改字符串,使用的函数如下:
方法或属性
用途
split()
将字符串分割为列表,是在正则表达式匹配的地方进行分割。
sub()
寻找所有匹配的子串,并将它们替换为指定的字符串
subn()
同sub()的行为相同,但是返回的是修改后的新字符串及替换次数。

分割字符串
正则表达式样式的split()函数在此样式匹配的地方对一个字符串进行分割,并返回这些片段组成的列表。它类似于字符串对象的split()函数,但是在定界符上的通用性更强,使得你可以通过(?)进行分割,而字符串的split()函数公支持空白或固定字符串的定界符。如你所料,存在一个模块级别的re.split()函数。
.split(string[,maxsplit=0])
上面函数使用匹配正则表达式的子串将字符串string分割,如果正同表达式中使用了捕获括号,则它们的内容也会作为返回列表的一部分返回。如果maxsplit不为0,则表示至多maxsplit个分割将会进行。
你可以通过使用maxsplit参数来限定分割的个数。当maxsplit不为0,至多maxsplit数量的分割会进行,原字符串剩余的部分将作为列表的最后一个元素而返回。下面的例子中,定界符是任何非字母字符的序列:
>>>p=re.compile(r'\W+')
>>>p.split('Thisisatest,shortandsweet,ofsplit().')
['This','is','a','test','short','and','sweet','of','split','']
>>>p.split('Thisisatest,shortandsweet,ofsplit().',3)
['This','is','a','test,shortandsweet,ofsplit().']
有时你不仅对定界符之间的内容感兴趣,而且需要知道定界符是什么。如果在正则表达式中使用了捕获括号,则它们的值会作为列表的一部分返回。比较下面不同的调用:
>>>p=re.compile(r'\W+')
>>>p2=re.compile(r'(\W+)')
>>>p.split('This...isatest.')
['This','is','a','test','']
>>>p2.split('This...isatest.')
['This','...','is','','a','','test','.','']

模块级别的函数re.split()添加正则表达式作为第一个参数,其他和split()相同:
>>>re.split('[\W]+','Words,words,words.')
['Words','words','words','']
>>>re.split('([\W]+)','Words,words,words.')
['Words',',','words',',','words','.','']
>>>re.split('[\W]+','Words,words,words.',1)
['Words','words,words.']

查找与替换
还有一项任务是查找样式匹配的字符串,并将这些子串替换为另外的字符串。sub()函数包含一个替换的值(它可以是字符串或者函数)和待处理的字符串。
.sub(replacement,string[,count=0])
返回string中最左边的匹配正则表达式的替换实例。如果样式没有匹配,将返回原字符串。可选参数count是最大替换次数,必须为非负数,默认值0表示替换所有匹配子串。
下面是一个简单的使用sub()函数的例子,它将色彩名称替换为单词colour:
>>>p=re.compile('(blue|white|red)')
>>>p.sub('colour','bluesocksandredshoes')
'coloursocksandcolourshoes'
>>>p.sub('colour','bluesocksandredshoes',count=1)
'coloursocksandredshoes'
subn()函数做相同的事情,但是返回一个包含两个元素的元组,包含新字符串和替换的个数:
>>>p=re.compile('(blue|white|red)')
>>>p.subn('colour','bluesocksandredshoes')
('coloursocksandcolourshoes',2)
>>>p.subn('colour','nocoloursatall')
('nocoloursatall',0)
仅仅当和前一个匹配不相邻时,空匹配才进行:
>>>p=re.compile('x*')
>>>p.sub('-','abxd')
'-a-b-d-'
如果待匹配参数是一个字符串,则包含的反斜杠转义都将进行,即,\n将转换成单个的转行字符,\r将转换为回车符,等等。未知的转义如\j将被保留。后向指示,如\6将被替换为相应分组匹配的子串。这将允许你在最终的字符串中合并原来文本的那些部分。
下面的例子匹配了后接包含在{,}中的字符串的单词部分,并将section替换为subxection:
>>>p=re.compile('section{([^}]*)}',re.VERBOSE)
>>>p.sub(r'subsection{\1}','section{First}section{second}')
'subsection{First}subsection{second}'
还存在一个语法来指示命名分组,它由(?P<name>...)语法来定义。\g<name>将使用分组名称name匹配的子串,而\g<number>使用对应的分组序号。因此\g<2>相当于\2,但是在像\g<2>0这样的替换字符串中将意义明确(\20会被解释为分组序号20而非分组2后面加一个字符0)。下面的替换作用相同,使用了字符串替换的三种形式:
>>>p=re.compile('section{(?P<name>[^}]*)}',re.VERBOSE)
>>>p.sub(r'subsection{\1}','section{First}')
'subsection{First}'
>>>p.sub(r'subsection{\g<1>}','section{First}')
'subsection{First}'
>>>p.sub(r'subsection{\g<name>}','section{First}')
'subsection{First}'
参数replacement也可以是一个函数,来为你提供更多的控制权。如果replacement是一个函数,它将在样式pattern的每一个非重叠匹配处被调用,每一次调用都将为这个函数传递该次匹配的MaatchObject参数,并使用这个信息来计算意欲进行的替换字符串,最终返回。
下面的例子中替换函数将十进制转换为十六进制:
>>>defhexrepl(match):
...
"Returnthehexstringforadecimalnumber"
...
value=int(match.group())
...
returnhex(value)
...
>>>p=re.compile(r'\d+')
>>>p.sub(hexrepl,'Call65490forprinting,49152forusercode.')
'Call0xffd2forprinting,0xc000forusercode.'
在使用模块级别中的函数re.sub()时,正则表达工样式将作为第一个参数传递进去,它可以以样式对象或者字符串的形式提供。如果你需要指定正则表达式的标志,必须将第一个参数写了样式对象,或者在字符串中使用嵌入的调整符,如sub("(?i)b+","x","bbbbBBBB")将返回'xx'。
常见的问题
对于一些应用来说,正则表达式是一个强大的工具,但是它们多多少少有些行为抽象,有时不会按照你设想的那样去工作。本节将指出最常见的一些陷阱。
使用字符串类中的方法
有时使用re模块是一个错误。如果你要匹配固定的一个字符串,或者一个单字符,并且没有使用任何re模块中的特性,如IGNORECASE标志,那么正则表达式的强大功能并没有必要。字符串类有一些函数能够进行这些操作,并且速度更快,因为它仅仅调用一个短短的为此目的优化过的C循环,而非一个庞大的、更加抽象的正则表达式引擎。
一个例子是将一个固定的字符串替换为另外一个字符串,比如,你可能想用deed来替换word。re.sub()可以完成它,但是考虑一下replace()函数。后者也可以在兔子中替换单词,比如将swordfish变为sdeedfish,但是原生的正则表达式也可以做这些(注意为了避免只替换一部分,正则表达式样式应该写为\bword\b,以使该单词在前后都有一个边界),后者显然比replace()做得更多。
另外一个常用的工作是将字符串中的某个字符删除,并替换为另外一个字符。你可以使用诸如re.sub('\n',' ',
S)这样的形式,但是translate()已经足够做这些了,并且比正则表达式的操作要快。
简言之,在使用re模块前,先考虑下你的问题能不用更快速简单的字符串函数来完成。
match()search()
match()函数仅仅检查正则表达式能否在字符串开始处匹配,而search()将扫描整个字符串来寻找匹配,应当时刻记住这个分别:如果在0处不能匹配,则match()返回none。
>>>printre.match('super','superstition').span()
(0,5)
>>>printre.match('super','insuperable')
None
另外,search()将扫描整个字符串,并报告第一次匹配。
>>>printre.search('super','superstition').span()
(0,5)
>>>printre.search('super','insuperable').span()
(2,7)
有时你会想使用re.match(),并在样式前添加.*,这时候应当用re.search()来替代。正则表达式编译器会对样式进行分析来提高处理速度,其中一个分析就是指出匹配的首字符必须是什么,例如,一个以Crow开始的样式必须在开始处匹配C,这种分析使得引擎快速扫描字符串来查找起始字符,只有在发现C时才匹配其后部分。
而添加.*则放弃了这种优化,使得先扫描所有字符串,再回头寻找其后的匹配。因此,应当使用re.search()。
贪婪匹配和非贪婪匹配
重复一个正则表达式时(如a*),导致的行为将尽可能地使用这个样式,这样的事实常常会为你在试图匹配成对的定界符时(如在HTML标签中的小括号)带来麻烦,如匹配HTML标签的简单正则表达式就不会再起作用,就是因为.*.的“贪婪”匹配特性。
>>>s='<html><head><title>Title</title>'
>>>len(s)
32
>>>printre.match('<.*>',s).span()
(0,32)
>>>printre.match('<.*>',s).group()
<html><head><title>Title</title>
上面的正则表达式匹配了<html>中的<,而.*部分匹配剩下的所有字符,样式中剩余的>不能匹配字符串结尾处(空白),因此正则表达式引擎回头重新匹配,直到寻找到>,最终的匹配为<是<html>中的左括号,>是</title>中的右括号——这当然不是你想要的。
在这种情况下,解决的办法是使用非贪婪匹配*?,+?,??或者{m,n}?,它们会匹配尽可能少的文本。在目前的例子中,>将在匹配<后尽可能快地匹配,当失败时,匹配引擎向后前进一个字符,再次尝试匹配,这将产生正确的结果:
>>>printre.match('<.*?>',s).group()
<html>
(要注意使用正则表达式来解析HTML或者XML是一个痛苦的过程,建议使用专门的HTML或XML解析模块。)
使用re.VERBOSE
到目前为止你可能已经注意到正则表达式形式十分紧密,因此难于阅读。正则表达式可能包含很多反斜杠、括号和元字符,难于阅读和理解。
对于这些正则表达式,在编译时指定re.VERBOSE标志会大有帮助,因为它允许你用更加清晰的形式来书写。
此标志有几种效果:不在字符集合中的空白将被忽略,这意味着dog| cat和dog|cat效果相同,但是[ab]仍然能够匹配字符a,b或者空白;另外,你可以在样式中添加注释,它从#开始直到行尾。当使用三个引号引起的字符串时,使得正则表达式的形式更加整洁:
pat=re.compile(r"""
\s*
#Skipleadingwhitespace
(?P<header>[^:]+)
#Headername
\s*:
#Whitespace,andacolon
(?P<value>.*?)
#Theheader'svalue--*?usedto
#losethefollowingtrailingwhitespace
\s*$
#Trailingwhitespacetoend-of-line
""",re.VERBOSE)
这比下面的形式可读性好多了:
pat=re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")

(完)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: