1.正则表达式

Python通过标准库中的re模块来支持正则表达式。

1. 正则表达式的基本符号

符号 说明
| 表示择一匹配的管道符号(|),也就是键盘上的竖线,表示一个“从多个模式中选择其一”的操作。
. 点号或者句点(.)符号匹配除了换行符\n 以外的任何字符。
^ 脱字符(^)或者特殊字符\A匹配字符串的开始位置,美元符号($)或者\Z匹配字符串的末尾位置。
[] 方括号([])匹配一对方括号中包含的任何字符。
- 连字符(-)连接,用于指定一个字符的范围,例如,A-Z、a-z 或者0-9
^[] 脱字符紧跟在左方括号后面,这个符号就表示不匹配给定字符集中的任何一个字符。例如:[^aeiou]匹配一个非元音字符
*,+,? 星号或者星号操作符(*)将匹配其左边的正则表达式出现零次或者多次;。加号(+)操作符将匹配一次或者多次出现的正则表达式;问号(?)操作符将匹配零次或者一次出现的正则表达式。
{N} {N}最终精确地匹配前面的正则表达式N次,{M,N}将匹配M~N 次出现。
\d,\w,\s 简单地使用d表示匹配任何十进制数字,字符(\w)能够用于表示全部字母数字的字符集,\s可以用来表示空格字符.
() 圆括号可以对正则表达式进行分组;匹配子组。
扩展表示法 扩展表示法,它们是以问号开始(?…)。

2. python实现正则表达式

本文将介绍两个主要的方法——match()和search(),以及compile()函数。
compile(pattern,flags = 0) 使用任何可选的标记来编译正则表达式的模式,然后返回一个正则表达式对象
match(pattern,string,flags=0) 尝试使用带有可选的标记的正则表达式的模式来匹配字符串。如果匹配成功,就返回匹配对象;如果失败,就返回None.
search(pattern,string,flags=0) 使用可选标记搜索字符串中第一次出现的正则表达式模式。如果匹配成功,则返回匹配对象;如果失败,则返回None

在模式匹配发生之前,正则表达式模式必须编译成正则表达式对象。由于正则表达式在执行过程中将进行多次比较操作,因此强烈建议使用预编译。而且,既然正则表达式的编译是必需的,那么使用预编译来提升执行性能无疑是明智之举。re.compile()能够提供此功能。compile()方法编译后得到正则表达式对象

除了正则表达式对象之外,还有另一个对象类型:匹配对象。这些是成功调用match()或者search()返回的对象。匹配对象有两个主要的方法:group()和groups()
group()要么返回整个匹配对象,要么根据要求返回特定子组。groups()则仅返回一个包含唯一或者全部子组的元组。如果没有子组的要求,那么当group()仍然返回整个匹配时,groups()
返回一个空元组。

match()函数试图从字符串的起始部分对模式进行匹配。如果匹配成功,就返回一个匹配对象;如果匹配失败,就返回None,匹配对象的group()方法能够用于显示那个成功的匹配。
search()的工作方式与match()完全一致,不同之处在于search()会用它的字符串参数,在任意位置对给定正则表达式模式搜索第一次出现的匹配情况。正则表达式对象方法使用可选的pos和endpos 参数来指定目标字符串的搜索范围。

from re import match
from re import search
from re import compile
import re
import os


def match_search_test():
    # match是从字符串的起始位置开始匹配(正常匹配)
    mt_str0 = match('foo', 'foo the distance link')
    if mt_str0:
        print 'foo the distance link 的匹配结果:%s' % (mt_str0.group())

    # match是从字符串的起始位置开始匹配(无法匹配)
    mt_str = match('foo', 'g foo the distance link')
    if mt_str:
        print 'g foo the distance link 的匹配结果:%s' % mt_str.group()

    m_str1 = search('foo', 'g foo the distance link')
    if m_str1:
        print 'g foo the distance link 的搜索结果:%s' % m_str1.group()

    # 预编译正则表达式pattern
    regex2 = compile('foo')
    m_str2 = regex2.search(string='g foo the distance link', pos=0, endpos=10)
    print 'g foo the distance link 的在1到10的搜索结果:%s' % m_str2.group()

    # 匹配多个字符串
    regex3 = compile('bat|bit|bet')  # 正则表达式对象
    m_str3 = regex3.search('fly!my bat')
    if m_str3:
        print 'fly!my bat的搜索结果:%s' % m_str3.group()

    # 匹配任何单个字符 点号(.)不能匹配一个换行符\n 或者非字符
    anyend = '.end'
    m_str4 = match(anyend, '.end')
    print '.end的匹配结果:%s' % m_str4.group()
    m_str5 = match(anyend, '\nend')
    print '\nend的匹配结果:%s' % str(m_str5 is not None)
    m_str6 = match(anyend, 'end')
    print 'end的匹配结果:%s' % str(m_str6 is not None)

    # 创建字符集[ ]
    regex7 = compile('[cr][23][dp][o2]')
    m_str7 = regex7.match('c3po')
    print 'c3po的匹配结果:%s' % m_str7.group()

    # 重复、特殊字符和分组
    regex8 = compile(r'(\w)+-(\d)+')
    m_str8 = regex8.match('abd-123')
    print 'abd-123的匹配结果:%s' % m_str8.group()
    m_str9 = match('\w+@(\w+\.)(\w+\.)\w+\.com','nobody@www.xxx.yyy.com')
    print 'nobody@www.xxx.yyy.com的匹配结果:%s' % m_str9.group()
    # 当正则表达式有圆括号括起来的部分时,就称为子组。 按顺序1,2,3,...子组。可以使用group(n) 来返回每个子组匹配上的字符串。
    # 同时,可以使用groups方法,返回匹配这些子组的字符串所组成的元组列表。
    print m_str9.group(1),m_str9.group(2),m_str9.groups()

    # 匹配字符串的起始和结尾以及单词边界    字符串前加r,正则表达式,不加无法匹配。
    m_str10 = search(r'\bthe', 'bit the dog')
    print 'bit the dog的搜索结果:%s' % m_str10.group()

findall()查询字符串中某个正则表达式模式全部的非重复出现情况。findall()总是返回一个列表。如果findall()没有找到匹配的部分,就返回一个空列表,但如果匹配成功,列表将包含所有成功的匹配部分(从左向右按出现顺序排列)。
finditer()与findall()函数类似但是更节省内存。

有两个函数/方法用于实现搜索和替换功能:sub()和subn()。两者几乎一样,都是将某字符串中所有匹配正则表达式的部分进行某种形式的替换。。subn()和sub()一样,但subn()还返回一个表示替换的总数,替换后的字符串和表示替换总数的数字一起作为一个拥有两个元素的元组返回。

如果给定分隔符不是使用特殊符号来匹配多重模式的正则表达式,那么re.split()与str.split()的工作方式相同,re.split()可以解决正则表达式匹配的问题。

def findall_finditer():
    s = 'This and That and. That and This'
    m_str1 = re.findall(r'(Th\w+) and (Th\w+)', s, re.I)
    print m_str1

    m_str2 = re.sub(r'GD|TC|BD', 'MT', 'The best Map provider is BD, TC and GD!')
    print m_str2
    m_str3 = re.subn(r'GD|TC|BD', 'MT', 'The best Map provider is BD, TC and GD!')
    print m_str3

    #\N 可以直接替换分组, 第二个参数表示将第2个分组和第1个分组调换位置
    m_str4 = re.sub(r'(\d{1,2})/(\d{1,2})/(\d{4})', r'\2/\1/\3', '9/18/1987')
    print m_str4

上边的程序例子中,部分在正则表达式前添加了r,例如r'\s+',被称为python原始字符串。使用这个的原因是:ASCII字符和正则表达式的特殊字符之间存在冲突,例如\b 表示ASCII 字符的退格符,但是\b同时也是一个正则表达式的特殊符号,表示匹配一个单词的边界。那么不加r时,要正确表达,就需要写成'\b'来转义\符号,使用r''表达方式则更简单了,不需要转义。

3.简单例子

3.1需求描述

这一部分,通过正则表达式完成一个十分简单的需求,在输入who命令时,返回类似如下结果:

natty    console  Apr 13 21:58 
natty    ttys000  Apr 13 22:10 

我们需要将结果按照登录名、用户登录的终端类型、用户登录的时间和地点来分列,最后打印输出一个列表。

3.2 分析和代码

如果使用简单的空格分割列的话,登陆时间项中也有空格,会造成结果错误,可以使用正则表达式。另外,执行系统命令,可以使用os.popen()来完成。str.rstrip()可以用来去除尾部的\n。分割列的是2个及以上的空格,所以程序如下:

# 按列格式化输出 who命令的执行结果:
def rewho():
    # os.popen()执行一个命令,并返回一个文件对象
    with os.popen('who') as f:
        for line in f.readlines():
            print re.split(r'\s\s+', line.strip())

输出结果:

['natty', 'console', 'Apr 13 21:58']
['natty', 'ttys000', 'Apr 13 22:10']

4.练习数据集生成

需求: 生成拥有三个字段的字符串,由一对冒号或者一对双冒号分隔。第一个字段是随机(32 位)整数,该整数将被转换为一个日期。下一个字段是一个随机生成的电子邮件地址。最后一个字段是一个由单横线(-)分隔的整数集。
程序如下:

import random
from time import ctime
from string import ascii_lowercase

tlds = ('com', 'edu', 'org', 'net', 'gov')

if __name__ == '__main__':
    # random.randrange(5, 11) 生成 >=5 并 <11的一个随机数 递增基数是默认值1
    # xrange() 函数用法与 range 完全相同,所不同的是生成的不是一个数组,而是一个生成器。
    with open('datafiles/regex_data_sample_gen.txt', 'w') as f:
        for i in xrange(random.randrange(7, 15)):
            # 如果在32位机器上,maxint就是-2**31~2**31-1,如果在64位机器上,maxint就是-2**63~2**63-1 [dtint = random.randrange(maxint)]
            # ctime()将一个int类型的unix时间戳转化为时间字符串。
            dint = random.randrange(2**31-1)
            dtstr = ctime(dint)
            llen = random.randrange(4, 8)
            dlen = random.randrange(llen, 13)
            login_list, dom_list = [], []
            # string.ascii_lowercase 是字母表中拥有26个小写字母的序列集合
            for j in range(llen):
                login_list.append(random.choice(list(ascii_lowercase)))
            for j in range(dlen):
                dom_list.append(random.choice(list(ascii_lowercase)))
            login = ''.join(login_list)
            dom = ''.join(dom_list)
            # choice() 方法返回一个列表,元组或字符串的随机项。 例如, choice([1, 2, 3, 5, 9]) :  2
            # join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串。   例如 str = 'Accenture'  '|+|'.join(list(str))
            # 得到:A|+|c|+|c|+|e|+|n|+|t|+|u|+|r|+|e
            dom_name = random.choice(tlds)
            f.write(
                "%s::%s@%s.%s::%d-%d-%d\n" % (dtstr, login, dom, dom_name, dint, llen, dlen)
            )
    f.close()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,780评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,424评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,397评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,576评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,997评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,945评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,107评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,850评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,625评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,804评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,285评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,613评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,291评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,164评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,963评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,096评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,886评论 2 278

推荐阅读更多精彩内容