`
frank-liu
  • 浏览: 1684200 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

regular expression tips: 基础知识

 
阅读更多

前言

    接触regular expression是从一个python的方法string.find()所引申出来的。对于一些普通的字符串元素查找和替换,感觉普通的find, replace等方法就已经足够了。可是在一些牵涉到复杂的应用里,还是非用regular expression不可。另外,一些web框架里url mapping的手段也大量的用到了regular expression。regular expression本质上就是字符串的模式搜索和匹配。这里有必要对最近常使用的一些方法做一个总结。虽然示例都是采用了python代码,但是使用其他的语言也有类似的方法,因为对于我们需要匹配的模式对所有语言来说都是一样的。

几个最常用的方法

    在python里,和regular expression打交道的主要就是re包。这里有最常用的几个方法,在我们做一些模式匹配的时候要用到。这里,几个常用的方法如下表:

方法名 说明
search

扫描整个字符串找到匹配的位置,并返回MatchObject对象。

如果没有找到匹配的则返回None.

match

如果0个或者多个在目标字符串开头的地方和表达式匹配,则返回对应的MatchObject对象。

否则返回None.

findall 按照从左到右的方式,查找字符串中所有不重叠的模式匹配。
finditer

和findall类似,不过返回一个所有匹配字符模式的迭代器,

可以通过访问迭代器的方式来访问所有匹配结果。

sub 将匹配的模式字符串替换成目标字符串。类似于replace方法。

    这里列出来的说明看起来有点抽象。那么我们就先来看一个简单的示例。

search

>>>import re
>>>m = re.search('foo', 'seafood')
>>> m.group()
'foo'
>>> m.start()
3
>>> m.end()
6

    我们先看search方法,这里前面的'foo'就是我们需要查找匹配的字符串。在目标字符串中,'seafood'中是中间部分和'foo'匹配。于是返回的MatchObject不是None。通过start, end方法我们可以分别找到匹配的字符串在目标串中的起始匹配下标和结束下标+1。这里有意思的地方就是'foo'在'seafood'中最后匹配的字符index是5,而这里m.end返回却是6。因为这里的结束索引6用在m[s:e]的时候,后面的结束索引是不包含在结果里的。如果我们这里取如下的方法:

>>> a = 'seafood'
>>> a[3:6]
'foo'

    用一个数学符号来概括的话,这里的[s:e]表达的是一个半开半闭区间,比如说这里应该是这样: [3, 6)。

match

    前面search的方法我们好理解,就是在一个字符串里找匹配的串。那么match方法表示什么意思呢?我们看如下的代码:

>>> import re
>>> match = re.match('foo', 'seafood')
>>> match is None
True

    很奇怪,这里和前面search方法传的参数一样,结果却显示match为None,也就是说根本没有找到匹配的。如果我们再修改一下:

>>> match = re.match('foo', 'food')
>>> match.group()
'foo'
>>> match.start()
0
>>> match.end()
3

    通过这部分代码,我们也就知道了,match要能找到匹配,必须要有一个条件就是它必须是和给定的目标串的开头匹配才行,如果开头没有匹配,就认为是没有匹配。

findall, finditer

    findall方法返回的主要是一个list,里面是匹配的串内容:

>>> match = re.findall('ab', 'abaabbaaabbb')
>>> type(match)
<type 'list'>
>>> for item in match:
...     print item
... 
ab
ab
ab

    这种方式返回的结果就是匹配的串。当然,在一些我们需要更多信息的地方,比如匹配的结果在目标串的某个具体index时,finditer是一个更好的选择。我们用finditer来实现类似的功能代码如下:

>>> match = re.finditer('ab', 'abaabbaaabbb')
>>> for item in match:
...     s = item.start()
...     e = item.end()
...     print 'Found "%s" at %d:%d' % (item.group(), s, e)
... 
Found "ab" at 0:2
Found "ab" at 3:5
Found "ab" at 8:10

    这里,finditer可以说是一个更加强大的findall。它在我们要找到所有或者多个匹配结果的地方使用很合适。

compile

    和前面几个方法不一样,前面的几个方法描述的是各种不同的匹配方式。这里,compile方法则是用来提高模式匹配的效率。我们前面的示例里,基本上是指定一个模式串和一个目标串之后就执行匹配方法。这种方式很简单直接,但是在某些情况下如果要考虑到效率的时候,我们可以考虑用compile方法来做一些优化。比如有的时候我们的这个模式串的匹配要使用的比较频繁或者我们有多个串要做匹配。我们来看看如下的代码:

import re
# generate list of expression objects
regexes = [re.compile(p) for p in ['this', 'that']]

text = 'Does this text match the pattern?'

print 'Text: %r\n' % text

for regex in regexes:
    print 'Seeking "%s" ->' % regex.pattern,
    
    match = regex.search(text)
    if match:
        print 'match! %s %s %s' % (match.string, match.start(), match.end())
    else:
        print 'no match'
     这里有意思的是我们的re.compile返回了一个expression object,然后我们再用这个expression object再执行search方法。执行的结果如下:
Text: 'Does this text match the pattern?'

Seeking "this" -> match! Does this text match the pattern? 5 9
Seeking "that" -> no match

regular expression符号说明

    前面我们举的例子实际上只是一个简单的字符串匹配,而且还是完整的匹配。从运用的角度来说,我们用一些简单的string.find等方法也可以达到同样的目的。regular expression和那些看似简单的方法的区别在于它有更强的表达能力。在很多需要满足各种灵活要求的时候,他们就可以派上用场。比如说我们需要匹配某些字符还要包含大小写,或者我们指定匹配多少个等等,这里我们就可以看到regular expression的强大了。我们分成几个部分来讨论。

基本符号

    常见的一些符号如下表:

符号 描述 示例
字符串常量 匹配完整的制定字符串 re.search('foo', 'foobar')
re1|re2 匹配表达式1或者表达式2 foo|bar
. 匹配任何字符,除了\n b.b
^ 匹配字符串的开头 ^Start
$ 匹配字符串的结尾 end$
* 匹配前面模式0到多次 [a-z]*
+ 匹配前面模式1到多次 [a-z]+
? 匹配前面模式0到1次 ab?
{N} 匹配前面模式N次 [0-9]{3}
{M, N} 匹配前面模式M到N次 [0-9]{2, 5}
[...] 匹配里面的单个字符 [acde]
[x-y] 匹配里面从x到y这个范围内的单个字符 [0-9]
[^...] 不匹配指定范围内的字符 [^abc], [^0-9]

    我们一个一个的看过来:

>>> match = re.search('foo|bar', 'foodbar')
>>> match.group()
'foo'
>>> match.start()
0
>>> match.end()
3
>>> match = re.search('foo|bar', 'bargine')
>>> match.group()
'bar'
>>> match.start()
0
>>> match.end()
3
    这里相当于一个表达或关系的符号,我们提供的一个或者多个模式,只要有一个匹配就可以了。match.group返回被匹配的那个模式。
>>> match = re.search('ab.', 'abcdefg')
>>> match.group()
'abc'
>>> match.start()
0
>>> match.end()
3
    这里的“.”符号可以匹配几乎任意的符号,所以当我们提供一个'abcdefg'这样的串时,表示匹配上了'abc'。

    我们再看看字符串的开始和结束的匹配,如果用过django做过web开发的话,会发现里面路径映射的时候要用到很多这样的符号。

 

>>> match = re.search('^goo', 'goodbye')
>>> match.group()
'goo'
>>> match = re.search('good$', 'sounds good')
>>> match.group()
'good'
    从前面我们可以看出,对于指定开头的search方法,从某种程度来说就相当于一个match方法。 

 

    另外,对于那些指定匹配次数的应用示例如下:

>>> match = re.findall('[0-9]*', '123abc456')
>>> match
['123', '', '', '', '456', '']
>>> match = re.findall('[0-9]+', '123abc456')
>>> match
['123', '456']
>>> match = re.findall('[0-9]?', '123abc456')
>>> match
['1', '2', '3', '', '', '', '4', '5', '6', '']
>>> match = re.findall('[0-9]{2}', '123abc456')
>>> match
['12', '45']
>>> match = re.findall('[^aeiou]', 'abcdefghijk')
>>> match
['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k']
    

除了以上这些基本的符号应用之外,还有一些特殊的符号应用可以保证我们写出更加精简的匹配模式。

一些特殊字符

    我们前面列举的一些字符可以满足很大一部分的要求,在某些情况下,一些特殊字符能够提供更加强大的支持。

符号 描述 示例
\d 匹配单个数字,和[0-9]一样(\D和其意思相反,表示不匹配任何数字) data\d+.txt
\w 匹配数字和字母,包括下划线,和[A-Za-z0-9_]表示一样(\W与此意相反) [A-Za-z_]\w+
\s 匹配任何空格类型的字符和[\n\t\r\v\f]一样 This\sis
\b 匹配每个词语的边界(指每个词语的开头和结尾两个部分) \bThe\b
\A\Z 匹配字符串的开头和结尾,与^$相同 \AMonth

    这些特殊字符的表示方式更简洁,我们看如下的示例:

>>> match = re.findall(r'\d+', '123abc234')
>>> match
['123', '234']
>>> match = re.findall(r'\w+', '123 abc, result 12.34 _123')
>>> match
['123', 'abc', 'result', '12', '34', '_123']

    这里要特别说明一下\b的用法,它和字符串的开始和结束容易混淆。实际上对于一个字符串来说,里面可能有多个单独的词语。在\b的用法里,每个单独的词语作为考察的边界。而对于字符的开始和结束符号^$来说,就是整个字符串的开始和结束。中间出现空格和其他词语都不在考察的范围。

>>> match = re.findall(r'\bt\w+', 'This is a test string, that contains 2 matches.')
>>> match
['test', 'that']
>>> match = re.findall(r'\w+t\b', 'at that mat t')
>>> match
['at', 'that', 'mat']
>>> match = re.findall(r't$', 'at that mat t')
>>> match
['t']
>>> match = re.findall(r'^t', 'this is a test string, that contains 2 matches.')
>>> match
['t']

    相信看完了上面这些比较代码之后,我们就知道该如何使用这两者了。 

greedy和nongreedy

    应用regular expression的时候还有一个需要考虑的就是greedy和nongreedy的属性。该怎么理解这个东西呢?官方的说明是,regular expression的模式匹配默认是贪婪的。就是说如果我们一个目标串和我们的模式匹配,则我们的表达式尽量匹配尽可能多的字符串。在某些情况下,我们需要调整一下。让我们先看一下示例:

>>> match = re.finditer('[ab]', 'abbaabbba')
>>> for item in match:
...     print item.group(), item.start(), item.end()
... 
a 0 1
b 1 2
b 2 3
a 3 4
a 4 5
b 5 6
b 6 7
b 7 8
a 8 9

    这里是我们要匹配字符a或者b,每次打印匹配的字符和匹配的范围。如果我们把前面的代码修改一下:

>>> match = re.finditer('a[ab]+', 'abbaabbba')
>>> for item in match:
...     print item.group(), item.start(), item.end()
... 
abbaabbba 0 9

     我们这里的模式是一个字母a,后面接1到多个字母a或者b。那么,ab就是一个符合我们匹配要求的。但是这里却返回了整个串。这就是greedy的意思。只返回匹配最长的那个串。那么,如果这里我们要返回最小的匹配呢?比如说前面示例里我只要返回一个尽量小的ab就可以了。那该怎么修改呢?

>>> match = re.finditer('a[ab]+?', 'abbaabbba')
>>> for item in match:
...     print item.group(), item.start(), item.end()
... 
ab 0 2
aa 3 5

    这里找到了索引在0和3开始的两个字串,而且都只是找到恰好匹配的。取消greedy特性的方法很简单,就是在设置多个元素属性的地方加一个问号(?)。

 

总结

    regular expression主要用于解决字符串匹配相关的问题,它本身很灵活,相关的匹配问题更多的归结为如何描述需要匹配的目的。这些更多的是和具体使用语言无关的。这里总结了几个基本的regular expression特性和用法。在后续文章里会讨论它的一些更加特殊的用法。

 

参考材料

Core python applications programming

The Python Standard Library by example

分享到:
评论
2 楼 frank-liu 2013-10-04  
这里都是python代码,我贴一部分代码的时候没有选择代码类型,默认的就变成java了。
1 楼 mikelij 2013-10-04  
怎么java的方法和python的很相似?都是python? 总结的挺好。

相关推荐

    eclipse regular expression 插件

    eclipse regular expression 插件

    regular expression library正则表达式库

    GNU Regex 程式库是 GNU 发展,提供操作比对 Regular Expression 文字字串的程式库,也就是使用 GNU Regex 程式库,可以作到以下的功能: 比对一字串是否完全与 Regular Expression 相幅合。 在一字串中寻找与 ...

    Regular Expression Pocket Reference.chm

    Regular Expression Pocket Reference

    Regular Expression Quick Reference v1.00

    Regular Expression Quick Reference

    Java2核心技术卷I+卷2:基础知识(第8版) 代码

    Regular Expressions 75 Chapter 2: XML 87 Introducing XML 88 Parsing an XML Document 93 Validating XML Documents 105 Locating Information with XPath 129 Using Namespaces 136 Streaming Parsers...

    Regular Expression 简介.pdf

    ### Regular Expression 简介 #### 一、何为 Regular Expression (正则表达式) 正则表达式(Regular Expression)是一种强大的文本模式匹配工具,在文本处理领域占据着极其重要的地位。它提供了一种简洁的方式来...

    Regular Expression Recipes for Windows Developers.pdf

    1. **基础知识**:正则表达式(Regular Expressions)是一种强大的文本处理工具,在字符串搜索和替换中有着广泛的应用。它们由一系列字符和特殊符号组成,用于匹配字符串中的模式。 2. **Windows开发环境下的使用**...

    正则表达式资料全集 Regular Expression Syntax Reference

    首先,"正则表达式30分钟入门教程(第二版)"为初学者提供了快速了解正则表达式基础知识的途径。通常,正则表达式由字符、元字符、量词和括号等组成,这些基础元素可以组合成复杂的模式来匹配特定的字符串。教程会介绍...

    RegAxe - Regular Expression Tester:用于测试正则表达式的工具。-开源

    RegAxe是用于测试正则表达式的工具。 您可以粘贴Java或PHP样式格式的正则表达式,如有必要,它将在执行测试之前自动转换。 找到的所有匹配项将被列出,并且相应的文本段落将在输入文本区域中突出显示。

    基于Regular Expression的数据匹配验证.pdf

    "基于Regular Expression的数据匹配验证" 基于Regular Expression的数据匹配验证是指使用Regular Expression(正则表达式)来对用户输入的数据进行匹配验证,以确保数据的正确性和合法性。在Web开发中,数据验证是...

    正则表达式grep[global search regular expression]

    ### 正则表达式grep[global search regular expression] #### 一、grep简介 `grep`是一种功能强大的文本搜索工具,可以使用正则表达式在文本中查找特定模式,并将匹配的行输出到标准输出。这一工具最初设计用于...

    Regular Expression Cookbook

    通过《正则表达式 Cookbook》,读者不仅可以学习到正则表达式的基础知识,还能了解到高级技巧和最佳实践,这对于任何需要处理字符串的开发者来说都是一本不可或缺的参考书。无论是初学者还是经验丰富的开发者,都能...

    regular expression

    正则表达式(Regular Expression,简称regex)是用于匹配字符串的一种模式,广泛应用于文本处理、数据提取、搜索替换等场景。在 Vim 编辑器中,正则表达式功能强大,能够帮助程序员和开发者在大型项目中高效地进行...

    java正则表达式: regular expression(一)

    本文实例源码 博文链接:https://gmf.iteye.com/blog/89077

    regular expression processor

    regular expression processor, 将正则表达式转换成NFA,接着讲NFA转换成DFA,并输出DFA。同时可以生成DOT文件,以提供给graphviz生成图形界面。

    Regular expression.pdf

    介绍正则表达式的英文slide 使用Python和Java实现相应的功能 文件已加密,可在下载后与我联系,免费获得解锁密码

    正则表达式(regular expression)

    正则表达式(regular expression)  原著:笑容  创作于:2004年05月03日 最后更新:2004年05月04日 21:12 引用地址:正则表达式(regular expression) 版权声明:使用创作公用版权协议 ...

    Oralce Regular Expression

    ### Oracle Regular Expression详解 #### 引言 Oracle正则表达式提供了一种强大的方式来识别文本体中的模式。模式描述了需要识别的文本外观,它可以是相当简单的(例如描述任何三个字母组成的单词),也可以是非常...

Global site tag (gtag.js) - Google Analytics