Python正则表达式详细剖析

JSON 2024-03-20 17:20:48 525

正则表达式

正则表达式(或 RE)指定与其匹配的一组字符串;该模块中的函数可让您检查特定字符串是否与给定的正则表达式匹配(或者给定的正则表达式是否与特定字符串匹配,这归结为同一件事)。下面sojson接下来举例和详细解析。

下表提供了可在正则表达式中使用的特殊模式匹配字符的列表和说明。

特殊
字符
描述
(点)在默认模式下,它匹配除换行符之外的任何字符。如果指定了 DOTALL 标志,则它匹配包括换行符在内的任何字符。
^(插入符。)匹配字符串或行的开头。例如 /^A/ 与“about Articles”中的“A”不匹配,但与“Articles of life”中的“A”匹配
$匹配字符串或行的结尾。例如,/e$/ 与“exact”中的“t”不匹配,但与“w3resource”中的“t”匹配。
*匹配前一个字符 0 次或多次。例如,/bo*/ 与“A bootable usb”中的“boo”和“A beautiful mind”中的“b”匹配,但与“A goingcertain”中的任何内容都不匹配。
+匹配前一个字符 1 次或多次。例如,/a+/ 匹配“Daniel”中的“a”以及“Daaam”中的所有 a
匹配前一个字符 0 次或 1 次。例如,/r?eu?/ 匹配“w3resource”中的“re”和“europe”中的“eu”。
*?, +?, ??“*”、“+”和“?” 预选赛都是贪婪的;它们匹配尽可能多的文本。有时这种行为是不需要的;如果 RE <.*> 与“ b ”匹配,它将匹配整个字符串,而不仅仅是“”。添加?在限定符使其以非贪婪或最小方式执行匹配之后;将匹配尽可能少的字符。使用 RE <.*?> 将仅匹配“”。
{米}指定  应匹配前一个 RE 的m 个副本;较少的匹配会导致整个 RE 不匹配。例如,b{5} 将恰好匹配五个“b”字符,而不是四个。
{m,n} 使生成的 RE 与前面的 RE 的m 到 n次重复相匹配 。例如,a{2,5} 将匹配 2 到 5 个“a”字符。省略 m 指定下限为零,省略 n 指定无限上限。例如,a{4,}b 将匹配“aaaab”或后跟“b”的一千个“a”字符,但不匹配“aaab”。
{m,n}?使生成的 RE 匹配 前面 RE 的m 到 n 次重复,尝试匹配 尽可能少的 重复。这是上一个限定符的非贪婪版本。例如,对于 6 个字符的字符串“aaaaaa”,a{3,5} 将匹配 5 个“a”字符,而 a{3,5}? 只会匹配 3 个字符。
\

要么转义特殊字符(允许您匹配“*”、“?”等字符),要么表示特殊序列;下面讨论特殊序列。

[]用于表示一组字符。在一组中:
  • 字符可以单独列出,例如 [amk] 将匹配“a”、“m”或“k”。
  • 字符范围可以通过给出两个字符并用“-”分隔来表示,例如 [az] 将匹配任何小写 ASCII 字母,[0-5][0-9] 将匹配所有两位数00 到 59,[0-9A-Fa-f] 将匹配任何十六进制数字。如果 - 被转义(例如 [a\-z])或者将其放置为第一个或最后一个字符(例如 [-a] 或 [a-]),则它将匹配文字“-”。
  • 特殊字符在集合内失去其特殊含义。例如,[(+*)] 将匹配任何文字字符“(”、“+”、“*”或“)”。
  • \w 或 \S(定义如下)等字符类也可以在集合内接受,尽管它们匹配的字符取决于 ASCII 模式还是 LOCALE 模式是否有效。
  • 不在某个范围内的字符可以通过补集来匹配。如果集合的第一个字符是“^”,则将匹配所有不在集合中的字符。例如,[^5] 将匹配除“5”之外的任何字符,[^^] 将匹配除“^”之外的任何字符。如果 ^ 不是集合中的第一个字符,则它没有特殊含义。
  • 要匹配集合内的文字“]”,请在其前面添加反斜杠,或将其放在集合的开头。例如, [()[\]{}] 和 []()[{}] 都将匹配括号。
|A|B(其中 A 和 B 可以是任意 RE)创建将匹配 A 或 B 的正则表达式。可以用“|”分隔任意数量的 RE。这样。这也可以在组内使用(见下文)。
(...)匹配括号内的任何正则表达式,并指示组的开始和结束;执行匹配后可以检索组的内容,并且可以稍后在字符串中使用 \number 特殊序列进行匹配,如下所述。要匹配文字“(”或“)”,请使用 \( 或 \),或将它们括在字符类中:[(], [)]。
(?...)这是一个扩展符号(“(”后面的“?”否则没有意义)。“?”后面的第一个字符决定了构造的含义和进一步的语法。扩展通常不会创建新组;而是创建新组。 (?P...) 是此规则的唯一例外。以下是当前支持的扩展。
(?aiLmsux)(来自集合“a”、“i”、“L”、“m”、“s”、“u”、“x”中的一个或多个字母。)该组与空字符串匹配;这些字母设置相应的标志:re.A(仅 ASCII 匹配)、re.I(忽略大小写)、re.L(区域设置相关)、re.M(多行)、re.S(点匹配所有) 、re.U(Unicode 匹配)和 re.X(详细),用于整个正则表达式。
(?:...)常规括号的非捕获版本。匹配括号内的任何正则表达式,但在执行匹配后无法检索该组匹配的子字符串或稍后在模式中引用。
(?imsx-imsx:...)(“i”、“m”、“s”、“x”集合中的零个或多个字母,可选地后跟“-”,后跟同一集合中的一个或多个字母。)这些字母设置或删除相应的标志:re.I(忽略大小写)、re.M(多行)、re.S(点匹配所有)和 re.X(详细),用于表达式的部分。
(?P<名称>...)

与常规括号类似,但组匹配的子字符串可以通过符号组名 name 访问。组名称必须是有效的 Python标识符,并且每个组名称只能在正则表达式中定义一次。符号组也是编号组,就像该组没有命名一样。

命名组可以在三种上下文中引用。如果模式是 (?P['"]).*?(?P=quote) (即匹配用单引号或双引号引起来的字符串):

引用组“引用”的上下文参考方法
以相同的模式本身
  • (?P=quote)(如图所示)
  • \1
处理匹配对象 m 时
  • m.group('报价')
  • m.end('quote') (等)
在传递给 re.sub() 的 repl 参数的字符串中
  • \g<引号>
  • \g<1>
  • \1
(?P=姓名)对命名组的反向引用;它匹配先前名为 name 的组所匹配的任何文本。
(?#...)一条评论; 括号中的内容将被忽略。
(?=...)如果...匹配下一个,则匹配,但不消耗任何字符串。这称为前瞻断言。例如,Isaac (?=Asimov) 仅当其后跟“Asimov”时才会匹配“Isaac”。
(?!...)如果...接下来不匹配则匹配。这是一个否定的前瞻断言。例如,Isaac (?!Asimov) 仅当后面没有“Asimov”时才会匹配“Isaac”。
(?<=...)

如果字符串中的当前位置前面有一个以当前位置结尾的 for ... 匹配项,则匹配。这称为积极的后向断言。(?<=abc)def 将在 'abcdef' 中找到匹配项,因为lookbehind 将备份 3 个字符并检查所包含的模式是否匹配。

>>> 进口重新
>>> m = re.search('(?<=abc)def', 'abcdef')
>>> m.group(0)
'定义'

此示例查找连字符后面的单词:

>>> m = re.search(r'(?<=-)\w+', '垃圾邮件蛋')
>>> m.group(0)
'蛋'
(?如果字符串中的当前位置前面没有匹配 ...,则匹配。这称为否定后向断言。与正向回顾断言类似,所包含的模式必须仅匹配某个固定长度的字符串。以否定后向断言开头的模式可能会在正在搜索的字符串的开头匹配。
(?(id/name)是模式|无模式)如果给定 id 或名称的组存在,则尝试使用 yes-pattern 进行匹配,如果不存在,则尝试使用 no-pattern 进行匹配。no-pattern 是可选的并且可以省略。例如, (<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$) 是一个较差的电子邮件匹配模式,它将与 '< user@host.com匹配>' 以及'user@host.com ',但不能与 '< user@host.com ' 或'user@host.com >' 一起使用。特殊序列由“\”和下面列表中的一个字符组成。如果普通字符不是 ASCII 数字或 ASCII 字母,则生成的 RE 将匹配第二个字符。例如,\$ 匹配字符“$”。
\数字匹配相同编号的组的内容。组从 1 开始编号。例如,(.+) \1 匹配“the the”或“55 55”,但不匹配“thethe”(请注意组后面的空格)。该特殊序列只能用于匹配前 99 组中的一组。如果number的第一位是0,或者number的长度是3个八进制数字,则不会被解释为组匹配,而是被解释为八进制值number的字符。在字符类的“[”和“]”内部,所有数字转义都被视为字符。
\A仅在字符串的开头匹配。
\b

匹配空字符串,但仅匹配单词的开头或结尾。单词被定义为单词字符的序列。

\B匹配空字符串,但仅当它不在单词的开头或结尾时才匹配。这意味着 r'  py \B' 匹配 '  python '、'py3'、'py2',但不匹配 '  py '、'py.' 或 'py!'。\B 与 \b 相反,因此 Unicode 模式中的单词字符是 Unicode 字母数字或下划线,尽管这可以通过使用 ASCII 标志来更改。如果使用 LOCALE 标志,则字边界由当前区域设置确定。
\d

匹配任何数字字符。相当于[0-9]。例如,/\d/ 或 /[0-9]/ 匹配“E2 表示第二个示例”中的“2”。

\D匹配任何非十进制数字的字符。这与 \d 相反。如果使用 ASCII 标志,则这相当于 [^0-9](但该标志影响整个正则表达式,因此在这种情况下,使用显式 [^0-9] 可能是更好的选择)。
\s

匹配任何空白字符(包括制表符、换行符、回车符、换页符、垂直制表符)。[\t\n\r\f\v]。
例如,/\s\w*/ 匹配“An apple”中的“apple”。

匹配 ASCII 字符集中被视为空白的字符;这相当于 [ \t\n\r\f\v]。

\S匹配任何不是空白字符的字符。这与 \s 相反。如果使用 ASCII 标志,这相当于 [^ \t\n\r\f\v] (但该标志影响整个正则表达式,因此在这种情况下使用显式 [^ \t\n\r\ f\v] 可能是更好的选择)。
\w

匹配 ASCII 字符集中被视为字母数字的字符;这相当于 [a-zA-Z0-9_]。如果使用 LOCALE 标志,则匹配当前区域设置中被视为字母数字的字符和下划线。

\W

匹配任何非单词字符,相当于 [^A-Za-z0-9_]。例如,/\W/ 或 /[^$A-Za-z0-9_]/ 匹配“150$”中的“$”

\Z仅在字符串末尾匹配。

比较 HTML 标签:

标签类型格式例子
开放标签<[^/>][^>]*>,<表>
关闭标签]+>
自我关闭<[^/>]+/>
所有标签<[^>]+>
# open tag
>>> import re
>>> re.search('<[^/>][^>]*>', '') != None
True
>>> import re
>>> re.search('<[^/>][^>]*>', '') != None
True
>>> import re
>>> re.search('<[^/>][^>]*>', '') != None
True
>>> import re
>>> re.search('<[^/>][^>]*>', '
') != None False # close tag >>> import re >>> re.search(']+>', '') != None True # self close >>> import re >>> re.search('<[^/>]+/>', '
') != None True

re.findall() 匹配字符串:

# split all string
>>> import re
>>> source = "split all string"
>>> re.findall('[\w]+', source)
['split', 'all', 'string']

# parsing python.org website
>>> import urllib
>>> import re
>>> x = urllib.urlopen('https://www.w3resource.org')
>>> html = x.read()
>>> x.close()
>>> print("open tags")
open tags
>>> re.findall('<[^/>][^>]*>', html)[0:2]
['', '']
>>> print("close tags")
close tags
>>> re.findall(']+>', html)[0:2]
['', '']
>>> print("self-closing tags")

组间比较:

# (...) group a regular expression
>>> import re
>>> mon = re.search(r'(\d{4})-(\d{2})-(\d{2})', '2018-09-01')
>>> mon
<_sre.SRE_Match object at 0x019A72F0>
>>> mon.groups()
('2018', '09', '01')
>>> mon.group()
'2018-09-01')
>>> mon.group(1)
'2018'
>>> mon.group(2)
'09'
>>> mon.group(3)
'01'

# Nesting groups
>>> import re
>>> mon = re.search(r'(((\d{4})-\d{2})-\d{2})', '2018-09-01')
>>> mon.groups()
('2018-09-01', '2018-09', '2018')
>>> mon.group()
'2018-09-01'
>>> mon.group(1)
'2018-09-01'
>>> mon.group(2)
'2018-09'
>>> mon.group(3)
'2018'

非捕获组:

# non capturing group
>>> import re
>>> url = 'http://w3resource.com/'
>>> mon = re.search('(?:http|ftp)://([^/\r\n]+)(/[^\r\n]*)?', url)
>>> mon.groups()
('w3resource.com', '/')

# capturing group
>>> import re
>>> mon = re.search('(http|ftp)://([^/\r\n]+)(/[^\r\n]*)?', url)
>>> mon.groups()
('http', 'w3resource.com', '/')

回溯参考:



# compare 'xx', 'yy'
>>> import re
>>> re.search(r'([a-z])\1$','xx') != None
True
>>> import re
>>> re.search(r'([a-z])\1$','yy') != None
True
>>> import re
>>> re.search(r'([a-z])\1$','xy') != None
False
# compare open tag and close tag
>>> import re
>>> pattern = r'<([^>]+)>[\s\S]*?'
>>> re.search(pattern, ' test ') != None
True
>>> re.search(pattern, '
test
') != None
True
>>> re.search(pattern, ' test ') != None
False


命名分组 (?P) :

# group reference ``(?P...)``
>>> import re
>>> pattern = '(?P\d{4})-(?P\d{2})-(?P\d{2})'
>>> mon = re.search(pattern, '2018-09-01')
>>> mon.group('year')
'2018'
>>> mon.group('month')
'09'
>>> mon.group('day')
'01'

# back reference ``(?P=name)``
>>> import re
>>> re.search('^(?P[a-z])(?P=char)','aa')
<_sre.SRE_Match object at 0x01A08660>

替换字符串:

# basic substitute
>>> import re
>>> res = "4x5y6z"
>>> re.sub(r'[a-z]',' ', res)
'4 5 6 '

# substitute with group reference
>>> import re
>>> date = r'2018-09-01'
>>> re.sub(r'(\d{4})-(\d{2})-(\d{2})',r'\2/\3/\1/',date)
'09/01/2018/'

# camelcase to underscore
>>> def convert(s):
...     res = re.sub(r'(.)([A-Z][a-z]+)',r'\1_\2', s)
...     return re.sub(r'([a-z])([A-Z])',r'\1_\2', res).lower()
...
>>> convert('SentenceCase')
'sentence_case'
>>> convert('SentenceSentenceCase')
'sentence_sentence_case'
>>> convert('SampleExampleHTTPServer')
'sample_example_http_server'
环视四周 :
符号比较方向
(?<=...)右到左
(?=...)左到右
(?!<...)右到左
(?!...)左到右
# basic
>>> import re
>>> re.sub('(?=\d{3})', ' ', '56789')
' 5 6 789'
>>> re.sub('(?!\d{3})', ' ', '56789')
'567 8 9 '
>>> re.sub('(?<=\d{3})', ' ', '56789')
'567 8 9 '
>>> re.sub('(?

匹配常用用户名或密码:

>>> import re
>>> re.match('^[a-zA-Z0-9-_]{3,16}$', 'Foo') is not None
True
>>> re.match('^\w|[-_]{3,16}$', 'Foo') is not None
True

匹配十六进制颜色值:

>>> import re
>>> re.match('^#?([a-f0-9]{6}|[a-f0-9]{3})$', '#ff0000')
<_sre.SRE_Match object at 0x019E7720>
>>> re.match('^#?([a-f0-9]{6}|[a-f0-9]{3})$', '#000000')
<_sre.SRE_Match object at 0x019E77A0>

匹配电子邮件:

>>> import re
>>> re.match('^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})
'citi.world@example.com')
<_sre.SRE_Match object; span=(0, 22), match='citi.world@example.com'>

# or

>>> import re
>>> example = re.compile(r'''^([a-zA-Z0-9._%-]+@
[a-zA-Z0-9.-]+
\.[a-zA-Z]{2,4})*$''', re.X)
>>> example.match('citi.world@example.citi.com')
<_sre.SRE_Match object; span=(0, 27), match='citi.world@example.citi.com'>
>>> example.match('citi%world@example.citi.com')
<_sre.SRE_Match object; span=(0, 27), match='citi%world@example.citi.com'>

匹配网址:

>>> import re
>>> example = re.compile(r'''^(https?:\/\/)? # match http or https
...             ([\da-z\.-]+)            # match domain
...             \.([a-z\.]{2,6})         # match domain
...             ([\/\w \.-]*)\/?$        # match api or file
...             ''', re.X)
>>> example.match('www.yahoo.com')
<_sre.SRE_Match object; span=(0, 13), match='www.yahoo.com'>
>>> example.match('http://www.example')
<_sre.SRE_Match object; span=(0, 18), match='http://www.example'>
>>> example.match('http://www.example/w3r.html')
<_sre.SRE_Match object; span=(0, 27), match='http://www.example/w3r.html'>
>>> example.match('http://www.example/w3r!.html')
>>> example
re.compile('^(https?:\\/\\/)?\n([\\da-z\\.-]+)\n\\.([a-z\\.]{2,6})\n([\\/\\w \\.-]*)\\/?$\n', re.VERBOSE)

匹配IP地址:


符号描述
[1]?[0-9][0-9]匹配 0-199 模式
2[0-4][0-9]匹配 200-249 模式
25[0-5]匹配 251-255 模式
(?:...)不抓组
>>> import re
>>> example = re.compile(r'''^(?:(?:25[0-5]
... |2[0-4][0-9]
... |[1]?[0-9][0-9]?)\.){3}
... (?:25[0-5]
... |2[0-4][0-9]
... |[1]?[0-9][0-9]?)$''', re.X)
>>> example.match('192.168.1.1')
<_sre.SRE_Match object at 0x0134A608>
>>> example.match('255.255.255.0')
<_sre.SRE_Match object at 0x01938678>
>>> example.match('172.17.0.5')
<_sre.SRE_Match object at 0x0134A608>
>>> example.match('256.0.0.0') is None
True

匹配Mac地址:

>>> import random
>>> mac = [random.randint(0x00, 0x6b),
... random.randint(0x00, 0x6b),
... random.randint(0x00, 0x6b),
... random.randint(0x00, 0x6b),
... random.randint(0x00, 0x6b),
... random.randint(0x00, 0x6b)]
>>> mac = ':'.join(map(lambda x: "%02x" % x, mac))
>>> mac
'05:38:64:60:55:63'
>>> import re
>>> example = re.compile(r'''[0-9a-f]{2}([:])
... [0-9a-f]{2}
... (\1[0-9a-f]{2}){4}
'', re.X) >>> example.match(mac) is not None 
True

词法分析器:

>>> import re
>>> from collections import namedtuple
>>> tokens = [r'(?P\d+)',
	  r'(?P\+)',
	  r'(?P-)',
	  r'(?P\*)',
	  r'(?P/)',
	  r'(?P\s+)']
>>> lex = re.compile('|'.join(tokens))
>>> Token = namedtuple('Token', ['type', 'value'])
>>> def tokenize(text):
	scan = lex.scanner(text)
	return (Token(m.lastgroup, m.group())
		for m in iter(scan.match, None) if m.lastgroup != 'WS')

>>> for _t in tokenize('9 + 5 * 2 - 7'):
	print(_t)

	
Token(type='NUMBER', value='9')
Token(type='PLUS', value='+')
Token(type='NUMBER', value='5')
Token(type='TIMES', value='*')
Token(type='NUMBER', value='2')
Token(type='MINUS', value='-')
Token(type='NUMBER', value='7')
>>> tokens
['(?P\\d+)', '(?P\\+)', '(?P-)', '(?P\\*)', '(?P/)', '(?P\\s+)']

版权所属:SO JSON在线解析

原文地址:https://www.sojson.com/blog/512.html

转载时必须以链接形式注明原始出处及本声明。

本文主题:

如果本文对你有帮助,那么请你赞助我,让我更有激情的写下去,帮助更多的人。

相关文章
JavaScript对象详细剖析
JavaScript对象详细剖析
Python元组剖析
如何解JSON数据(详细解答)
Python元组剖析
TCP 和 UDP协议详细讲解,优缺点分讲解
Maven的Mirror和Repository 的详细讲解
HttpClient 获取详细的头信息
MySQL备份方案之Xtrabackup详细解答
Linux 安装 Redis 详细步骤讲解
最新文章
文件上传漏洞与防御 4058
前端构建工具选型指南:Webpack、Vite、Rollup、esbuild 深度对比 1444
物联网时代2026年时序数据库选型指南 1151
SaaS行业面临AI挑战:从“无限复用”到“灵活适应” 1269
神经网络:从构造到模型训练全链路解析 1168
一文吃透 Redis 核心存储结构:ziplist、listpack 与哈希表扩容 / 并发查询 1593
Linux sudo提权完整指南:从基础用法到生产级安全配置 691
XSS 和 CSRF 的本质区别及开发防御全解析 772
JVM垃圾回收(GC)全维度解析:从原理到调优实战 813
Linux动静态库与ELF加载全解析:从实操制作到底层原理 912
最热文章
免费天气API,天气JSON API,不限次数获取十五天的天气预报 783114
最新MyEclipse8.5注册码,有效期到2020年 (已经更新) 711464
苹果电脑Mac怎么恢复出厂系统?苹果系统怎么重装系统? 679993
Jackson 时间格式化,时间注解 @JsonFormat 用法、时差问题说明 562673
我为什么要选择RabbitMQ ,RabbitMQ简介,各种MQ选型对比 512621
Elasticsearch教程(四) elasticsearch head 插件安装和使用 484794
Jackson 美化输出JSON,优雅的输出JSON数据,格式化输出JSON数据... ... 302947
Java 信任所有SSL证书,HTTPS请求抛错,忽略证书请求完美解决 247433
Elasticsearch教程(一),全程直播(小白级别) 233097
谈谈斐讯路由器劫持,你用斐讯路由器,你需要知道的事情 228329
支付扫码

所有赞助/开支都讲公开明细,用于网站维护:赞助名单查看

查看我的收藏

正在加载... ...