加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

正则表达式和python的re模块

发布时间:2020-12-14 01:38:51 所属栏目:百科 来源:网络整理
导读:正则表达式和python的re模块 By钟桓 9月 4 2014更新日期:9月 4 2014 文章目录 1. 什么是正则表达式 2. 元字符使用一览表: 3. 字符转义 4. 重复 5. 字符类 6. 分支条件 7. 分组 8. re模块 8.0.1. compile 8.0.2. match和search 8.0.3. split 8.0.4. findall

正则表达式和python的re模块

9月 4 2014更新日期:9月 4 2014

文章目录
  1. 1.什么是正则表达式
  2. 2.元字符使用一览表:
  3. 3.字符转义
  4. 4.重复
  5. 5.字符类
  6. 6.分支条件
  7. 7.分组
  8. 8.re模块
    1. 8.0.1.compile
    2. 8.0.2.match和search
    3. 8.0.3.split
    4. 8.0.4.findall
    5. 8.0.5.finditer
    6. 8.0.6.sub
    7. 8.0.7.subn
  • 9.参考资料
  • 什么是正则表达式

    在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。正则表达式就是记录文本规则的代码,换句话说,正则表达式是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”)。模式描述在搜索文本时要匹配的一个或多个字符串。

    字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等等。字符串是0个或更多个字符的序列。文本也就是文字,字符串。说某个字符串匹配某个正则表达式,通常是指这个字符串里有一部分(或几部分分别)能满足表达式给出的条件。

    假设你要在英文小说中查找Hi,那么使用的正则表达式就是Hi,这个很简单吧,不过,通常处理正则表达式的工具(例如后面会提到的python的re模块)会提供忽略大小写的选项。

    不幸的是,很多单词里都包含了很多单词里包含hi这两个连续的字符,比如him,history,high等等。用hi来查找的话,这里边的hi也会被找出来。如果要精确地查找hi这个单词的话,我们应该使用bhib

    b是正则表达式规定的一个特殊代码(这里称为元字符,metacharacter),代表着单词的开头或结尾,也就是单词的分界处。

    假设你要找的Hi后面不远处有一个ZH,那么可以用bhib.*bZHb.

    这里,.是另一个元字符,匹配除了换行符以外的任意字符。*同样是元字符,不过它代表的不是字符,也不是位置,而是数量——它指定*前边的内容可以连续重复使用任意次以使整个表达式得到匹配,例如,zo* 能匹配 "z" 以及 "zoo"等 。

    如果同时使用其它元字符,我们就能构造出功能更强大的正则表达式。比如下面这个例子:
    0dd-dddddddd匹配这样的字符串:以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字(也就是中国的电话号码。当然,这个例子只能匹配区号为3位的情形)。

    现在你已经知道几个很有用的元字符了,如b,.,*,还有d.正则表达式里还有更多的元字符,比如s匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等。w匹配字母或数字或下划线或汉字等。

    下面来看看更多的例子:
    baw*b匹配以字母a开头的单词——先是某个单词开始处(b),然后是字母a,然后是任意数量的字母或数字(w*),最后是单词结束处(b)。
    好吧,现在我们说说正则表达式里的单词是什么意思吧:就是不少于一个的连续的w。不错,这与学习英文时要背的成千上万个同名的东西的确关系不大 :)
    d+匹配1个或更多连续的数字。这里的+是和*类似的元字符,不同的是*匹配重复任意次(可能是0次),而+则匹配重复1次或更多次。
    bw{6}b匹配刚好6个字符的单词。


    元字符使用一览表:

    上面介绍了部分的metacharacter,下面给出元字符一览表,使用时可以查找。

    字符

    说明

    将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,“n”匹配字符“n”。“n”匹配换行符。序列“”匹配“”,“(”匹配“(”。

    ^

    匹配输入字符串开始的位置。如果设置了RegExp对象的Multiline属性,^ 还会与“n”或“r”之后的位置匹配。

    $

    匹配输入字符串结尾的位置。如果设置了RegExp对象的Multiline属性,$ 还会与“n”或“r”之前的位置匹配。

    零次或多次匹配前面的字符或子表达式。例如,zo 匹配“z”和“zoo”。等效于 {0,}。

    +

    一次或多次匹配前面的字符或子表达式。例如,“zo+”与“zo”和“zoo”匹配,但与“z”不匹配。+ 等效于 {1,}。

    ?

    零次或一次匹配前面的字符或子表达式。例如,“do(es)?”匹配“do”或“does”中的“do”。? 等效于 {0,1}。

    {n}

    n是非负整数。正好匹配n次。例如,“o{2}”与“Bob”中的“o”不匹配,但与“food”中的两个“o”匹配。

    {n,}

    n是非负整数。至少匹配n次。例如,“o{2,}”不匹配“Bob”中的“o”,而匹配“foooood”中的所有 o。“o{1,}”等效于“o+”。“o{0,}”等效于“o ”。

    {n,m}

    Mn是非负整数,其中n<=m。匹配至少n次,至多m次。例如,“o{1,3}”匹配“fooooood”中的头三个 o。’o{0,1}’ 等效于 ‘o?’。注意:您不能将空格插入逗号和数字之间。

    ?

    当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是“非贪心的”。“非贪心的”模式匹配搜索到的、尽可能短的字符串,而默认的“贪心的”模式匹配搜索到的、尽可能长的字符串。例如,在字符串“oooo”中,“o+?”只匹配单个“o”,而“o+”匹配所有“o”。

    .

    匹配除“n”之外的任何单个字符。若要匹配包括“n”在内的任意字符,请使用诸如“[sS]”之类的模式。

    (pattern)

    匹配pattern并捕获该匹配的子表达式。可以使用$0…$9属性从结果“匹配”集合中检索捕获的匹配。若要匹配括号字符 ( ),请使用“(”或者“)”。

    (?:pattern)

    匹配pattern但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用“or”字符 (|) 组合模式部件的情况很有用。例如,’industr(?:y|ies) 是比 ‘industry|industries’ 更经济的表达式。

    (?=pattern)

    执行正向预测先行搜索的子表达式,该表达式匹配处于匹配pattern的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,’Windows (?=95|98|NT|2000)’ 匹配“Windows 2000”中的“Windows”,但不匹配“Windows 3.1”中的“Windows”。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

    (?!pattern)

    执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配pattern的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,’Windows (?!95|98|NT|2000)’ 匹配“Windows 3.1”中的 “Windows”,但不匹配“Windows 2000”中的“Windows”。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

    x|y

    匹配xy。例如,’z|food’ 匹配“z”或“food”。’(z|f)ood’ 匹配“zood”或“food”。

    [xyz]

    字符集。匹配包含的任一字符。例如,“[abc]”匹配“plain”中的“a”。

    [^xyz]

    反向字符集。匹配未包含的任何字符。例如,“[^abc]”匹配“plain”中的“p”。

    [a-z]

    字符范围。匹配指定范围内的任何字符。例如,“[a-z]”匹配“a”到“z”范围内的任何小写字母。

    [^a-z]

    反向范围字符。匹配不在指定的范围内的任何字符。例如,“[^a-z]”匹配任何不在“a”到“z”范围内的任何字符。

    b

    匹配一个字边界,即字与空格间的位置。例如,“erb”匹配“never”中的“er”,但不匹配“verb”中的“er”。

    B

    非字边界匹配。“erB”匹配“verb”中的“er”,但不匹配“never”中的“er”。

    cx

    匹配x指示的控制字符。例如,cM 匹配 Control-M 或回车符。x的值必须在 A-Z 或 a-z 之间。如果不是这样,则假定 c 就是“c”字符本身。

    d

    数字字符匹配。等效于 [0-9]。

    D

    非数字字符匹配。等效于 [^0-9]。

    f

    换页符匹配。等效于 x0c 和 cL。

    n

    换行符匹配。等效于 x0a 和 cJ。

    r

    匹配一个回车符。等效于 x0d 和 cM。

    s

    匹配任何空白字符,包括空格、制表符、换页符等。与 [ fnrtv] 等效。

    S

    匹配任何非空白字符。与 [^ fnrtv] 等效。

    t

    制表符匹配。与 x09 和 cI 等效。

    v

    垂直制表符匹配。与 x0b 和 cK 等效。

    w

    匹配任何字类字符,包括下划线。与“[A-Za-z0-9]”等效。

    W

    与任何非单词字符匹配。与“[^A-Za-z0-9]”等效。

    xn

    匹配n,此处的n是一个十六进制转义码。十六进制转义码必须正好是两位数长。例如,“x41”匹配“A”。“x041”与“x04”&“1”等效。允许在正则表达式中使用 ASCII 代码。

    num

    匹配num,此处的num是一个正整数。到捕获匹配的反向引用。例如,“(.)1”匹配两个连续的相同字符。

    n

    标识一个八进制转义码或反向引用。如果 n前面至少有n个捕获子表达式,那么n是反向引用。否则,如果n是八进制数 (0-7),那么n是八进制转义码。

    nm

    标识一个八进制转义码或反向引用。如果 nm前面至少有nm个捕获子表达式,那么nm是反向引用。如果 nm前面至少有n个捕获,则n是反向引用,后面跟有字符m。如果两种前面的情况都不存在,则 nm匹配八进制值nm,其中nm是八进制数字 (0-7)。

    nml

    n是八进制数 (0-3),ml是八进制数 (0-7) 时,匹配八进制转义码nml

    un

    匹配n,其中n是以四位十六进制数表示的 Unicode 字符。例如,u00A9 匹配版权符号 (?)。


    字符转义

    如果你想查找元字符本身的话,比如你查找.,或者,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用来取消这些字符的特殊意义。因此,你应该使用.和。当然,要查找本身,你也得用.

    例如:deerchao.net匹配deerchao.netC:Windows匹配C:Windows


    重复

    正则表达式第一件能做的事是能够匹配不定长的字符集,而这是其它能作用在字符串上的方法所不能做到的。 不过,如果那是正则表达式唯一的附加功能的话,那么它们也就不那么优秀了。它们的另一个功能就是你可以指定正则表达式的一部分的重复次数。

    就像前面介绍的元字符*.*并不匹配字母字符 “*”;相反,它指定前一个字符可以被匹配零次或更多次,而不是只有一次。

    上面我们的元字符表把大部分元字符都说了,这里我们抽取出重复限定符。

    代码/语法 说明
    * 重复零次或更多次
    + 重复一次或更多次
    ? 重复零次或一次
    {n} 重复n次
    {n,} 重复n次或更多次
    {n,m} 重复n到m次

    再举个例子,ca?t 将匹配 “ct” (0 个 “a” 字符) 或 “cat” (1 个 “a”);
    有了上面的这些限定元字符,可以很好得处理重复情况,只要运用得当。


    字符类

    要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?

    很简单,你只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母,[.?!]匹配标点符号(.或?或!)。

    我们也可以轻松地指定一个字符范围,像[0-9]代表的含意与d就是完全一致的:一位数字;同理[a-z0-9A-Z_]也完全等同于w(如果只考虑英文的话)。

    下面是一个更复杂的表达式:(?0d{2}[) -]?d{8}。

    “(”和“)”也是元字符,后面的分组节里会提到,所以在这里需要使用转义。

    这个表达式可以匹配几种格式的电话号码,像(010)88886666,或022-22334455,或02912345678等。我们对它进行一些分析吧:首先是一个转义字符(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字(d{2}),然后是)或-或空格中的一个,它出现1次或不出现(?),最后是8个数字(d{8})。


    分支条件

    不幸的是,刚才那个表达式也能匹配010)12345678或(022-87654321这样的“不正确”的格式。要解决这个问题,我们需要用到分枝条件。正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。听不明白?没关系,看例子:

    0d{2}-d{8}|0d{3}-d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)。

    (?0d{2})?[- ]?d{8}|0d{2}[- ]?d{8}这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。

    d{5}-d{4}|d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成d{5}|d{5}-d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。


    分组

    我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作(后面会有介绍)。

    (d{1,3}.){3}d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:d{1,3}匹配1到3位的数字,(d{1,3}.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(d{1,3})。

    IP地址中每个数字都不能大于255. 经常有人问我,01.02.03.04 这样前面带有0的数字,是不是正确的IP地址呢? 答案是: 是的,IP 地址里的数字可以包含有前导 0 (leading zeroes).

    不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:((2[0-4]d|25[0-5]|[01]?dd?).){3}(2[0-4]d|25[0-5]|[01]?dd?)。

    理解这个表达式的关键是理解2[0-4]d|25[0-5]|[01]?dd?,这里我就不细说了,你自己应该能分析得出来它的意义。


    re模块

    Python中得正则表达式(regular expression)模块,即re模块,功能还是很强大的。在介绍re之前,先看下面这部分。

    正则表达式使用反斜杠” “来代表特殊形式或用作转义字符,这里跟Python的语法冲突,因此,Python用” “表示正则表达式中的” “,因为正则表达式中如果要匹配” “,需要用来转义,变成” “,而Python语法中又需要对字符串中每一个进行转义,所以就变成了” “。

    上面的写法是不是觉得很麻烦,为了使正则表达式具有更好的可读性,Python特别设计了原始字符串(raw string),需要提醒你的是,在写文件路径的时候就不要使用raw string了,这里存在陷阱。raw string就是用’r’作为字符串的前缀,如 r”n”:表示两个字符””和”n”,而不是换行符了。Python中写正则表达式时推荐使用这种形式。

    下面来看一下re模块的几个函数:

    compile

    re.compile(strPattern[,flag]):

    这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。
    第二个参数flag是匹配模式,取值可以使用按位或运算符’|’表示同时生效,比如re.I | re.M。
    另外,你也可以在regex字符串中指定模式,
    比如re.compile(‘pattern’,re.I | re.M)与re.compile(‘(?im)pattern’)是等价的。
    可选值有:


    re.I(IGNORECASE): 忽略大小写(括号内是完整写法,下同)
    re.M(MULTILINE): 多行模式,改变'^'和'$'的行为(参见上图) re.S(DOTALL): 点任意匹配模式,改变'.'的行为 re.L(LOCALE): 使预定字符类 w W b B s S 取决于当前区域设定 re.U(UNICODE): 使预定字符类 w W b B s S d D 取决于unicode定义的字符属性 re.X(VERBOSE): 详细模式。这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释。


    其实compile方法的作用,不是很明显,因为下面的两个得到的结果等价。调用compile后,返回RegexObject对象,可以用该对象调用macth()和search()等匹配方法。

    import re
    
    pattern = r"hi";
    string = "hi,jack";
    
    prog = re.compile(pattern);
    result1 = prog.match(string);
    print "result1: ",result1.group();
    
    result2 = re.match(pattern,string);
    print "result2: ",result2.group();

    输出的结果是一样的。这里match下面会介绍,它是一个匹配的方法,group方法后面也会介绍,它这里输出的时匹配的内容。可以试验一下,就明白他两是等效的。

    match和search

    两个方法都是进行匹配时调用的。但他们有不同之处。

    Python提供了两种不同的原始操作:match和search。match是从字符串的起点开始做匹配,而search(perl默认)是从字符串做任意匹配。

    例子1

    import re
    
    result1 = re.match("c","abcde");
    if(result1):
        print("result1:"+result1.group());
    else:
        print("nothing");
    
    result2 = re.search("c","abcde");
    if(result2):
        print("result2:"+result2.group());

    输出:

    nothing

    result2:c

    例子2

    import re
    
    result1 = re.match("a","abcde");
    if(result1):
        print("result1:"+result1.group());
    else:
        print("nothing");
    
    result2 = re.search("a","abcde");
    if(result2):
        print("result2:"+result2.group());

    输出:

    result1:a

    result2:a

    例子3

    match函数可以设置匹配开始的位置,下面分别从0,1,2位置开始匹配。当然也可以设置终止的位置,具体可以查API文档。

    import re
    
    pattern = re.compile("c");
    
    result1 = pattern.match("abcde",0);
    if(result1):
        print("result1:"+result1.group());
    else:
        print("result1: nothing");
    
    result2 = pattern.match("abcde",1);
    if(result2):
        print("result2:"+result2.group());
    else:
        print("result2: nothing");
    
    result3 = pattern.match("abcde",2);
    if(result3):
        print("result3:"+result3.group());
    else:
        print("result3: nothing");

    输出:

    result1: nothing

    result2: nothing

    result3:c

    split

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

    通过正则表达式将字符串分离。如果用括号将正则表达式括起来,那么匹配的字符串也会被列入到list中返回。maxsplit是分离的次数,maxsplit=1分离一次,默认为0,不限制次数。

    看一下例子:

    import re    
    print re.split('W+','Words,words,words.')

    输出:

    [‘Words’,‘words’,‘’]

    思考一下为什么输出会是这个? 查看一下W的作用,注意W是大写的。

    findall

    re.findall(pattern,flags=0)

    找到 RE 匹配的所有子串,并把它们作为一个列表返回。这个匹配是从左到右有序地返回。如果无匹配,返回空列表。

    import re
    print re.findall("d","1a2b3c4d");

    输出:

    [‘1’,‘2’,‘3’,‘4’]

    finditer

    re.finditer(pattern,flags=0)

    找到 RE 匹配的所有子串,并把它们作为一个迭代器返回。这个匹配是从左到右有序地返回。如果无匹配,返回空列表。

    import re
    
    it = re.finditer(r"d+","123abc456efg789hij")
    for match in it:
        print match.group()

    输出:

    123
    456
    789

    sub

    sub(pattern,repl,count=0,flags=0)

    其用途是用来替换匹配成功的字串,被替换成repl。值得一提的时,这里的repl不仅仅可以是字符串,也可以是方法。

    首先看下字符串的时候,被匹配的字符串就会被替换成为repl。

    import re
    print re.sub(r'sANDs',' & ','Baked Beans And Spam',flags=re.IGNORECASE)

    输出:

    Baked Beans & Spam

    可以使用id或g<id>、g<name>引用分组.

    当repl是方法的时候。这个方法应当只接受一个参数(Match对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)。

    import re
    
    def dashrepl(matchobj):
        if matchobj.group(0) == '-': return ' '
        else: return '-'
    
    print  re.sub('-{1,2}',dashrepl,'pro----gram-files')
    print  re.sub('-{1,'pro----gram-files')

    输出:

    pro—gram files

    subn

    subn(repl,string[,count]) |re.sub(pattern,count]):
    多返回 (sub(repl,count]),替换次数)。

    import re
    print re.subn(r'sANDs',flags=re.IGNORECASE)


    参考资料

    1. 正则表达式30分钟入门教程(推荐)
    2. 正则表达式语法
    3. Python-re模块
    4. 深入浅出之正则表达式(推荐)
    5. 正则表达式re模块详解(推荐)
    6. Python正则表达式操作指南(推荐)
    7. python正则表达式教程

    (编辑:李大同)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

      推荐文章
        热点阅读