正则表达式

正则表达式(称为RE,或正则,或正则表达式模式)本质上是嵌入在Python中的一种微小的、高度专业化的编程语言,可通过 re 模块获得。

简单模式

正则表达式用于对字符串进行操作,因此我们将从最常见的任务开始:匹配字符

匹配字符:匹配不同的字符集合是正则表达式可以做的第一件事

元字符是 []

[abc],[a-c],[0-9],[abc] \作为元字符在中括号内失效,[^5] 匹配非'5'的字符,[5^] 将匹配 '5''^'

元字符是反斜杠,\

反斜杠来移除它们的特殊含义:\[\\

'\' 开头的特殊序列表示通常有用的预定义字符集

  • \d

    匹配任何十进制数字;这等价于类 [0-9]

  • \D

    匹配任何非数字字符;这等价于类 [^0-9]

  • \s

    匹配任何空白字符;这等价于类 [ \t\n\r\f\v]转义符在中括号中不失效吗

  • \S

    匹配任何非空白字符;这相当于类 [^ \t\n\r\f\v]

  • \w

    匹配任何字母与数字字符;这相当于类 [a-zA-Z0-9_]

  • \W

    匹配任何非字母与数字字符;这相当于类 [^a-zA-Z0-9_]

元字符是 .

它匹配除换行符之外的任何内容,并且有一个可选模式( re.DOTALL )甚至可以匹配换行符

重复:指定正则的某些部分必须重复一定次数

元字符是 *

一个逐步的例子将使这更加明显。 让我们考虑表达式 a[bcd]*b。 这个正则匹配字母 'a',类 [bcd] 中的零或多个字母,最后以 'b' 结尾。 现在想象一下这个正则与字符串 'abcbd' 匹配。

步骤 匹配 说明
1 a 正则中的 a 匹配。
2 abcbd 引擎尽可能多地匹配 [bcd]* ,直到字符串结束。
3 失败 引擎尝试匹配 b ,但是当前位置位于字符串结束,所以匹配失败。
4 abcb 回退一次,[bcd]* 少匹配一个字符。
5 失败 再次尝试匹配 b , 但是当前位置是最后一个字符 'd'
6 abc 再次回退,所以 [bcd]* 只匹配 bc
6 abcb 再试一次 b 。 这次当前位置的字符是 'b' ,所以它成功了。

元字符是 +

ca+t 将匹配 'cat' (1 个 'a'),'caaat' (3 个 'a'),但不会匹配 'ct'

重复限定符。 问号字符 ? 匹配一次或零次

home-?brew 匹配 'homebrew''home-brew'

重复限定符是 {m,n}

a/{1,3}b 将匹配 'a/b''a//b''a///b' 。 它不匹配没有斜线的 'ab',或者有四个的 'a////b', {0,}* 相同, {1,} 相当于 +{0,1}? 相同。 最好使用 *+? ,只要因为它们更短更容易阅读。

使用正则表达式

re 模块提供了正则表达式引擎的接口,允许你将正则编译为对象,然后用它们进行匹配

编译正则表达式

正则表达式被编译成模式对象,模式对象具有各种操作的方法,例如搜索模式匹配或执行字符串替换

p = re.compile('ab*')

re.compile() 也接受一个可选的 flags 参数,用于启用各种特殊功能和语法变体, p = re.compile('ab*', re.IGNORECASE)

正则作为字符串传递给 re.compile() ,将正则放在字符串中可以使 Python 语言更简单,但有一个缺点:反斜杠灾难。解决方案 r"\n" 是一个包含 '\''n' 的双字符字符串,而 "\n" 是一个包含换行符的单字符字符串。

应用匹配

一旦你有一个表示编译正则表达式的对象,你用它做什么? 模式对象有几种方法和属性。

方法 / 属性 目的
match() 确定正则是否从字符串的开头匹配。
search() 扫描字符串,查找此正则匹配的任何位置。
findall() 找到正则匹配的所有子字符串,并将它们作为列表返回。
finditer() 找到正则匹配的所有子字符串,并将它们返回为一个 iterator
>>> import re
>>> p = re.compile('[a-z]+')
>>> p
re.compile('[a-z]+')
>>> p.match("")
>>> print(p.match(""))
None
>>> m = p.match('tempo')
>>> m
<re.Match object; span=(0, 5), match='tempo'>

现在你可以检查 匹配对象 以获取有关匹配字符串的信息。 匹配对象实例也有几个方法和属性;最重要的是:

方法 / 属性 目的
group() 返回正则匹配的字符串
start() 返回匹配的开始位置
end() 返回匹配的结束位置
span() 返回包含匹配 (start, end) 位置的元组

尝试这些方法很快就会清楚它们的含义:

group() 返回正则匹配的子字符串。 start()end() 返回匹配的起始和结束索引。 span() 在单个元组中返回开始和结束索引。 由于 match() 方法只检查正则是否在字符串的开头匹配,所以 start() 将始终为零。 但是,模式的 search() 方法会扫描字符串,因此在这种情况下匹配可能不会从零开始。:

>>>

>>> print(p.match('::: message'))
None
>>> m = p.search('::: message'); print(m)
<re.Match object; span=(4, 11), match='message'>
>>> m.group()
'message'
>>> m.span()
(4, 11)

在实际程序中,最常见的样式是在变量中存储 匹配对象,然后检查它是否为 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(r'\d+')
>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
['12', '11', '10']

在这个例子中需要 r 前缀,使字面为原始字符串字面,因为普通的“加工”字符串字面中的转义序列不能被 Python 识别为正则表达式,导致 DeprecationWarning 并最终产生 SyntaxError。 请参阅 反斜杠灾难

findall() 必须先创建整个列表才能返回结果。 finditer() 方法将一个 匹配对象 的序列返回为一个 iterator

>>>

>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')
>>> iterator  
<callable_iterator object at 0x...>
>>> for match in iterator:
...     print(match.span())
...
(0, 2)
(22, 24)
(29, 31)

模块级别函

>>> print(re.match(r'From\s+', 'Fromage amk'))
None
>>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998')  
<re.Match object; span=(0, 5), match='From '>

编译标志

这是一个可用标志表,以及每个标志的更详细说明。

Flag 含义
ASCII, A 使几个转义如 \w\b\s\d 匹配仅与具有相应特征属性的 ASCII 字符匹配。
DOTALL, S 使 . 匹配任何字符,包括换行符。
IGNORECASE, I 进行大小写不敏感匹配。
LOCALE, L 进行区域设置感知匹配。
MULTILINE, M 多行匹配,影响 ^$
VERBOSE, X (为 '扩展') 启用详细的正则,可以更清晰,更容易理解。

re.I | re.M 设置 IM 标志

这里的正则使用 re.VERBOSE;看看阅读有多容易?: charref = re.compile(r""" &[#] # Start of a numeric entity reference ( 0[0-7]+ # Octal form | [0-9]+ # Decimal form | x[0-9a-fA-F]+ # Hexadecimal form ) ; # Trailing semicolon """, re.VERBOSE) 如果没有详细设置,正则将如下所示: charref = re.compile("&#(0[0-7]+" "|[0-9]+" "|x[0-9a-fA-F]+);")

更多元字符

Crow|Servo 将匹配 'Crow''Servo',而不是 'Cro''w''S''ervo'

print(re.search('^From', 'From Here to Eternity')) <re.Match object; span=(0, 4), match='From'> print(re.search('^From', 'Reciting From Memory')) None `

print(re.search('}$', '{block}')) <re.Match object; span=(6, 7), match='}'> >>> print(re.search('}$', '{block} ')) None >>> print(re.search('}$', '{block}\n')) <re.Match object; span=(6, 7), match='}'> `

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353