工具:正则表达式工具 Match Tracer
https://rubular.com/
https://pythex.org/

概述

正则表达式(Regular Expression)又称规则表达式,是处理字符串的有力工具,是对字符串操作的一种逻辑公式。
正则表达式的本质是用事先定义好的一些特定字符组成的“规则字符串”对字符串的一种过滤逻辑。“规则字符串可以包括普通字符(如a~z之间的英文字母)和特殊字符(称为“元字符”)。
例如正则表达式“0\d{2,3}-d{7,8}”包括普通数字和匹配数字的元字符“\d”,可以过滤或提取字符串中包含的固定电话号码,如“010-88888888” “0711-7777777”等
与Python提供的字符串处理函数相比,正则表达式提供了更加强大的处理功能,可以快速、准确地完成复杂的查找、替换等处理任务;其灵活性、逻辑性、功能性强,而且能用极简单的方式实现字符串的复杂控制
正则表达式处理字符串的过程
(1)编写正则表达式
(2)使用正则表达式引擎对正则表达式进行编译,得到正则表达式对象
(3)通过正则表达式对字符串进行匹配(或过滤),得到匹配(或过滤)结果

使用正则表达式提取字符串中的日期

import re
str='今天是2019-05-01,劳动节!'
reg='\d{2,4}-\d{1,2}-\d{1,2}'
res=re.findall(reg,str) #find all 查找所有匹配结果
print("日期",res)
#
日期 ['2019-05-01']

正则表达式语法

1、正则表达式组成

正则表达式的构造方法和数学表达是的构造方法一样,是用多种元字符与运算发组合在一起创建一个表达是。
组成正则表达式的可以是单个字符、字符集合、字符范围、字符间的选择或者他们之间的任意组合

(1)普通字符

正则表达式中的普通字符包括以下几项
1)英文字母:26个大些英文字母A~Z和26个小写英文字母a~z
2)汉字:Unicode字符集中包括的汉字
3)数字:0~9的10个数字
4)标点符号:如“:” “,”等标点符号
5)其他符号:如“\n”(换行符)“\t”(制表符)等非打印字符

(2)特殊字符

特殊字符指一些有特殊含义的字符。特殊字符一般具有通用性,如"\w"可以表示1位字母、数字、下划线
正则表达式中常用特殊字符包括字符匹配模式、定位符(描述字符串或单词的边界)和限定符(匹配次数)等

字符匹配模式

语法 备注 解释 正则表达式 字符串 匹配结果
. 任单 匹配除换行符(\n)外的任意单个字符 a.c abcalc abc,alc
\ 转义 表示位于\之后的转义字符 a\.c a.cde a.c
| 匹配位于|之前或之后的字符 ab|cd abcd ab,cd
[] 间单 匹配位于[]之中的任意一个字符 a[bcd]e abeade abe,ade
[a-c]同[abc] 范围 匹配指定范围的任意字符 [abc] ace a,c
[^a-c]同[^abc] 以外 匹配指定范围外的任意字符 [^abc] ace e
() 略迷 将()中的内容作为一个整体对待 (abc)d abcd abc
\d 1数 匹配1位数字 a\dc a1cabc a1c
\D 1非数 与\d的含义相反 a\DC a1cabc abc
\w 字母、数字、下划线 匹配1位数字、字母和下划线 a\wc abca1c abc,a1c
\W 与\w的含义相反 a\Wc abca c a c
\s 匹配1位空白字符:<空格>,\t,\n,\r,\f,\v a\sb a\nba\tb ‘a\nb’,‘a\tb’
\S 与\s的含义相反 ab\Sd abcd abcd

补充:在findall中,()是接收匹配结果保存到列表中,但是貌似findall有一个bug,当出现多个()的时候,会生成一个元组。可以使用之后提到的(?:)等代替不需要当作结果看待的方法。
Python 正则 findall 多规则匹配问题 结果是元组 解决过程
定位符

语法 备注 解释 正则表达式 字符串 匹配结果
^ 句首 匹配以^后面的字符或模式开头的字符串 ^abc abccc abc
$ 句末 匹配以$前面的字符或模式结束的字符串 abc$ aaabc abc
\b r、前后s 匹配单词头或单词尾 r’\babc\b’ d abc d abc
\B 与\b的含义相反 r’\Babc\B’ dabcd abc

限定符

语法 备注 解释 正则表达式 字符串 匹配结果
* 前1字符0+ 匹配*前一个字符出现0次或者多次 abc* ababcabccc ab,abc,abccc
+ 前1字符1+ 匹配+前一个字符出现1次或多次 abc+ ababcabccc abc,abccc
? 01 匹配?前一个字符只能出现0次或1次 abc? ababc ab,abc
{m} 匹配前一个字符只能出现m次 ab{2}c abcabbcabbbc abbc
{m,} 至少 匹配前一个字符至少出现m次 ab{2,}c abcabbcabbbc abbc,abbbc
{m,n} 匹配前一个字符出现m~n次 ab{1,3}c abcabbcabbbc abc,abbc,abbbc

扩展语法

除上述特殊语法外,Python还允许正则表达式进行语法扩展,以实现更加复杂的字符串处理功能
pattern:模式
零宽断言,后部不能脱离前部使用,否则会触发多余括号

语法 备注 解释 正则表达式 字符串 匹配结果
(?#pattern) 表示注释 excel(?#EX cell,单元格) excel电子表格 excel
(?:pattern) 全返 匹配但不捕获该匹配的自模式 win(?:2010)或(?:2010)win win2010或2010win win2010或2010win
(?=pattern) 后,外返 用于正则表达式之后,如果=后的内容在字符串中出现则匹配,但不返回=之后的内容 win(?=2010) win2010 win
(?!pattern) 后,非,外返 用于正则表达式之后,如果!后的内容在字符串中不出现则匹配,但不返回!之后的内容 win(?!2010) win1998 win
(?<=) 前<,外返 用于正则表达式之前,如果<=后的内容在字符串中出现则匹配,但不返回<=之后的内容 (?<=2010)win 2010win win
(?<!pattern) 前,非,外返 用于正则表达式之前,如果<!后的内容在字符串中不出现则匹配,但不返回<!之后的内容 (?<!2010)win 1998win win

匹配的贪婪模式与非贪婪模式

贪婪模式与非贪婪模式影响的是被量词修饰的子表达式的匹配行为:
(1)贪婪模式:在整个表达式匹配成功的前提下,尽可能多的匹配
(2)非贪婪模式:在整个表达式匹配成功的前提下,尽可能少的匹配
默认是贪婪模式,在量词后面直接加上一个问号就是非贪婪模式

import re
str="<div></div><div>监督学习</div><div>非监督学习</div><div>半监督学习</div>"
pat1=re.compile('<div>.*</div>')
res1=pat1.findall(str)
print("贪婪匹配:",res1)
pat2=re.compile('<div>.*?</div>')
res2=pat2.findall(str)
print("非贪婪匹配:",res2)
#
贪婪匹配: ['<div></div><div>监督学习</div><div>非监督学习</div><div>半监督学习</div>']
非贪婪匹配: ['<div></div>', '<div>监督学习</div>', '<div>非监督学习</div>', '<div>半监督学习</div>']

.*意味着0或多个换行符外的字符
?意味着思考

使用正则表达式模块处理字符串

Python自1.5版本起增加了Re模块,使Python拥有全部的正则表达式功能
Python标准库Re模块提供两种使用正则表达式处理字符串的方法:
(1)直接调用Re模块中的函数
(2)将正则表达式编译成正则表达式对象后再调用正则表达式对象中的函数
这两种方法的使用形式基本相同,但后者提供了更多、更强大的功能,如可以提高字符串的处理速度等

Re模块中的常用函数

1.匹配函数

(1)re.match()函数

尝试从字符串的起始位置匹配一个模式,若匹配成功则返回一个匹配对象;否则返回None。
match:匹配
pattern:正则表达式
flags:可选标志位

res=re.match(pattern,str,flags=0)

正则表达式常用标志位

修饰符 备注 描述
re.I 不区分大小写 使匹配对大小写不敏感
re.L 做本地化识别(locale-aware)匹配
re.M 多行匹配,影响^和$
re.S 匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符。这个标志影响\w、\W、\b、\B
re.X ^匹配字符串和每行的行首,$匹配字符串和每行的行尾

可以使用匹配对象的函数group(num)、groups()、groupdict()来获取匹配结果
(1)group(0):返回包括整个表达式的字符串
(2)group(n1,n2,…):返回一个包含多个组号对应值得元组
(3)groups():返回一个包含所有小组字符串的元组
(4)groupdict():返回一个包含所有经命名匹配小组的字典

使用re.match()函数提取中国国际长途电话的各部分

import re
str='0086-010-88668866'
mat=re.match(r"(?P<nation>\d{4,})-(?P<zone>\d{3,4})-(?P<number>\d{7,8})",str,re.M|re.I)
if mat:print("mat.group(0):",mat.group(0))print("mat.group(1):",mat.group(1))print("mat.group(2):",mat.group(2))print("mat.group(3):",mat.group(3))print("mat.group(1,2,3):",mat.group(1,2,3))print("mat.groups():",mat.groups())print("mat.groupdict():",mat.groupdict())
else:print("match匹配不成功")
#
mat.group(0): 0086-010-88668866
mat.group(1): 0086
mat.group(2): 010
mat.group(3): 88668866
mat.group(1,2,3): ('0086', '010', '88668866')
mat.groups(): ('0086', '010', '88668866')
mat.groupdict(): {'nation': '0086', 'zone': '010', 'number': '88668866'}

(2)re.search()函数

扫描整个字符串并返回第一个成功的匹配对象
res=re.search(pattern,str,flags=0)

import re
str="David的QQ电子邮箱:122345@qq.com"
sea=re.search(r"(?P<user>\d+)@(?P<website>\w+).(?P<extension>\w+)",str,re.M|re.I)
if sea:print("sea.group(0):",sea.group(0))print("sea.group(1,2,3):",sea.group(1,2,3))print("sea.groups():",sea.groups())print("sea.groupdict():",sea.groupdict())
else:print("search匹配不成功!")
#
sea.group(0): 122345@qq.com
sea.group(1,2,3): ('122345', 'qq', 'com')
sea.groups(): ('122345', 'qq', 'com')
sea.groupdict(): {'user': '122345', 'website': 'qq', 'extension': 'com'}

若此处将re.search()换成re.match()则返回None

import re
str='a0086-010-88668866'
mat=re.search(r"(?P<nation>\d{4,})-(?P<zone>\d{3,4})-(?P<number>\d{7,8})",str,re.M|re.I)
print(mat)
#
<re.Match object; span=(1, 18), match='0086-010-88668866'>

查找函数

(1)re.findall()函数

在字符串中找到正则表达式匹配的所有子字符串,并返回一个列表;
如果没有找到匹配,则返回空列表。
res=re.findall(pattern,str,flags=0)

re.match()和re.search()返回一次匹配结果,re.findall()返回所有匹配结果

使用re.findall()函数查找字符串中所有单词

import re
res=re.findall("\w+","hallo everybody, are you ok?")
print("匹配结果:",res)
#
匹配结果: ['hallo', 'everybody', 'are', 'you', 'ok']

(2)re.finditer

在字符串中找到正则表达式匹配的所有子字符串,并作为一个迭代器返回
res=re.finditer(pattern,str,flag=0)

import re
str="of the people , for the people , by the people"
res=re.finditer("\w+ \w+ people",str)
for s in res:print(s.group())print(list(res))
#
of the people
[<re.Match object; span=(16, 30), match='for the people'>, <re.Match object; span=(33, 46), match='by the people'>]

这里有个怪现象,res只能迭代一次,用完为止,所以当s被调用一次的里面,list把之后的内容一下子迭代完了,就没东西了,于是跳出了for

3.替换函数

Re模块中用于替换的函数有re.sub()和re.subn()
re.sub()将pattern的匹配项用repl替换,返回新字符串
res=re.sub(pattern,repl,str[,count=0])
resubn(),返回新字符串和替换次数的二元组

import re
phoneNumber="400-669-5566#中国银行信用卡客服专线电话"
num1=re.sub("#.*$","",phoneNumber)
print("使用sub()替换结果:",num1)
num2=re.subn("#.*$","",phoneNumber)
print("使用subn()替换结果:",num2)
#
使用sub()替换结果: 400-669-5566
使用subn()替换结果: ('400-669-5566', 1)

4.分割函数

通过指定分隔符对字符串进行分割并返回包含分割结果的列表
res=re.split(pattern,str[,maxsplit=0,flags=0])

import re
str="道可道,非常道;名可名,非常名。无名天地之始,有名万物之母。故常无欲以观其妙;常有欲以观其徼。此两者同出而异名,同谓之玄。玄之又玄,众妙之门。"
strList=re.split("\w+",str)
print("分割结果:",strList)
strList=re.split("\W+",str)
print("分割结果:",strList)
#
分割结果: ['', ',', ';', ',', '。', ',', '。', ';', '。', ',', '。', ',', '。']
分割结果: ['道可道', '非常道', '名可名', '非常名', '无名天地之始', '有名万物之母', '故常无欲以观其妙', '常有欲以观其徼', '此两者同出而异名', '同谓之玄', '玄之又玄', '众妙之门', '']

末尾一个小尾巴

5.编译函数 compile的真谛

我刚才看到一篇文章,上面说到了关键,就是compile,不是说每次写的时候不用再写一长串,不然r=^…$,每次re.match(r,s),同样效果,而是因为compile,例如正则表达式到底是什么东西,其实是一个引擎,输入正则表达式,转换成机器码,机器码对输入字符串进行处理。compile的关键就是他的结果已经是机器码,每次调用只需要处理机器码对字符串,而不再用正则表达式对机器码。理论上更加节省时间。就例如i=l[0]之后只需要对i做对比,而不用对l[0]做对比,要知道每次l[0]他先要找到l,再找index0。而把他先赋值给i,就直接找i就行。至于到底节省了多少时间,后面用到i越多,就越能展现出其价值!

for i in range(1000):re.findall("(?<!\d)[1-9]\d{4,9}(?!\d)","3219321,123,12389890432758974892375,10000")
#2.42 ms
for i in range(1000):r.findall("3219321,123,12389890432758974892375,10000")
#1.79 ms

用于编译正则表达式,生成一个正则表达式对象,供函数match()、search()、findall()等使用
re.compile(pattern[,flags])

import re
pattern=re.compile('wr\w+ \w+')
res=pattern.findall("Frankly, in the opinion of the Joint Chiefs of Staff, this strategy would involve us in the wrong war, at the wrong place, at the wrong time, and with the wrong enemy")
if res:print("匹配结果:",res)
else:print("匹配无结果!")
#
匹配结果: ['wrong war', 'wrong place', 'wrong time', 'wrong enemy']
re.compile('wr\w+ \w+').findall("Frankly, in the opinion of the Joint Chiefs of Staff, this strategy would involve us in the wrong war, at the wrong place, at the wrong time, and with the wrong enemy")
#
1.96 µs ± 126 ns
re.findall("wr\w+ \w+","Frankly, in the opinion of the Joint Chiefs of Staff, this strategy would involve us in the wrong war, at the wrong place, at the wrong time, and with the wrong enemy")
#
1.92 µs ± 24.8 ns

并没有快多少

常用正则表达式

校验数字的常用正则表达式

校验 正则表达式 备注
数字 ^[0-9]*$ ^\d*$
n位数字 ^\d{n}$ ^[0-9]{n}$
至少n位数字 ^\d{n,}$ 同上
m~n位数字 ^\d{m,n}$ 同上
带m~n位小数的正数或负数 ^(\-)?\d+(\.\d{m,n})?$ (\-|\+)?\d+(.\d{m,n})?$
带2位小数的正实数 ^[0-9]+(\.[0-9]{2})?$ ^(\+)?\d+\.\d{2}$
非零的正整数 ^\+?[1-9][0-9]*$ ^+?[1-9]\d*$
非零的负整数 ^-[1-9][0-9]*$ ^-[1-9]\d*$
非负整数 ^[1-9]\d* 0$
非正整数 ^-[1-9][0-9]*|0$ ^(-\d+)|0$
非负浮点数 ^\d+(\.\d+)?$或^[1-9]\d*.\d*|0.\d*[1-9]\d|0?.0+|0$ (?!\+?^\.$)(^\+?\d*.\d*$)
非正浮点数 ^((-\d+(.\d+)?)|(0+(\.0+)?))$ (?!^-?\.$)((^-\d*\.\d*$)|(0*\.0*))
浮点数 ^(-?\d+)(\.\d+)?$ (?!^(\+|-)?\.$)(^(\+|-)?\d*\.\d*$)

直接使用re.函数,match最高效,search 980ns vs 1.2µs
[0-9]与\d通用,前者效率高一点点1.03 µs vs 1.11 µs/1.05

在浮点数的时候,这里出现了第一个难题:"."这个即便是在语法中也是不承认的。在网上查询获得:

(?!PatternB)PatternA
^(?!^\d*$)([a-zA-Z0-9-]{2,15})$
(PatternA|PatternB)
(^$|\d{7,16})

正则表达式实现与或非关系

书上一些写法真的很头疼,首先正则表达式的则就是规则的意思,所以是应对规则写对应的表达式,我觉得这里的浮点数就是python的可识别转化的输入法是,0.、.0都是浮点数,反之1不是浮点数。而书中的例子真的很蛋疼!
于是最后两行那个很无聊的写法就不写进来了

校验字符的常用正则表达式

校验 正则表达式 备注
汉字 ^[\u4e00-\u9fa5]{0,}$ ^[一-龥]*$
长度为m~n的所有字符 ^.{m,n}$
由英文字母组成的字符串 ^[A-Za-z]+$ ^[A-z]+$
由大写英文字母组成的字符串 ^[A-Z]+$
由小写英文字母组成的字符串 ^[a-z]+$
由数字和大小写英文字母组成的字符串 ^[A-Za-z0-9]+$或^[A-Za-z0-9]{m,n}$
中文、英文、数字、下划线 ^[\u4E00-\u9FA5A-Za-z0-9_]+$

之前就隐约觉得,[A-z],里面是对应的字符集,尝试果然
re.match(’^[一-龥]*$’,“王大全”)

在网上发现word的正则表达式同时可以用[一-﨩]0xfa29
实际上,字符联系到龥,之后字符码序断断续续,一直到﨩

中日韩象形文字
中日韩统一表意文字
范围: 4E00—9FFF龍 9f8d  龎 9f8e  龏 9f8f  龐 9f90  龑 9f91
龒 9f92  龓 9f93  龔 9f94  龕 9f95  龖 9f96
龗 9f97  龘 9f98  龙 9f99  龚 9f9a  龛 9f9b
龜 9f9c  龝 9f9d  龞 9f9e  龟 9f9f  龠 9fa0
龡 9fa1  龢 9fa2  龣 9fa3  龤 9fa4  龥 9fa5  龦 9fa6  龧 9fa7  龨 9fa8  龩 9fa9  龪 9faa
龫 9fab  龬 9fac  龭 9fad  龮 9fae  龯 9faf
龰 9fb0  龱 9fb1  龲 9fb2  龳 9fb3  龴 9fb4
龵 9fb5  龶 9fb6  龷 9fb7  龸 9fb8  龹 9fb9
龺 9fba  龻 9fbb  龼 9fbc  龽 9fbd  龾 9fbe
龿 9fbf  鿀 9fc0  鿁 9fc1  鿂 9fc2  鿃 9fc3
鿄 9fc4  鿅 9fc5  鿆 9fc6  鿇 9fc7  鿈 9fc8
鿉 9fc9  鿊 9fca  鿋 9fcb  鿌 9fcc  鿍 9fcd
鿎 9fce  鿏 9fcf  鿐 9fd0  鿑 9fd1  鿒 9fd2
鿓 9fd3  鿔 9fd4  鿕 9fd5以上是像是文字样的私用区
范围: E000—F8FF e815-- e864
貌似是部首中日韩兼容象形文字-Fa29
中日韩兼容表意文字豈
范围: F900—FAFF
豈 f900 - 﨩 fa29
之后还有很多文字,例如:
里 中日韩兼容象形文字-F9e9
度娘的话,他显示的是91cc
"里"=="里"
False
至少字形上,两者貌似是一样的

至于期间还有什么,请查询:Unicode范围

关于是否能直接写[A-z]我去查了下字符集,不行,Z和a之间还有几个符号
\u只识别四位,准确的说是后面4位,例如\u10400会自动识别为\u0400

特殊要求校验的常用正则表达式

校验 正则表达式 备注
E-mail地址 ^\w+@(\w+.)+\w+$ ^[A-Za-z0-9_]+@[A-Za-z0-9_]+.[A-Za-z0-9_]+$
域名 ^[A-Za-z0-9][-A-Za-z0-9_]{0,62}(\.[A-Za-z0-9][-A-Za-z0-9_])+.?$ 书中的都是/,不只是编辑错误还是别的原因,这个域名规则比较迷
网址 [A-Za-z]+://[^\s]* ^[A-Za-z]+://(\w\.)+[A-Za-z]+(/\w\.?)+$
手机号码 ^(13[0-9]|14[579]|15[0-35-9]|166|17[0135-8]|18[0-9]|19[89])\d{8}$ 书中这里两处编辑错误,其中一点[0-3,5-9],逗号是纯符号,没功能,即“15,”也被正则通过。我之后的代码错误中出现过0-3-9,第一个“-”和0、3配对成为功能键,第二个“-”配对失败,成为字符
国内电话号码 ^0\d{2,3}-\d{7,8}$ 书中在次编辑错误,如果不加首尾符号,使用search或findall,010-123456789他会把末尾按8取出,逻辑上来说,其实是应该返回一个7,返回一个8,例如从一堆字符中筛选可能的电话号码,那么有方法同时返回7和8吗???
身份证号 ^\d{15} \d{18}$
IP地址 ^\d{3}.\d{3}.\d{3}.\d{3}$
邮政编码 [1-9]\d{5}(?!\d)$ 不明白,首先我这边首位是0的,再者,第一次在书中看到(?!)却是用在这种情况,看不明白
QQ [1-9][0-9]{4}
日期格式 ^\d{4}-\d{2}-\d{2}$
re.match('^[A-Za-z]+://([\w-]+\.)+[A-Za-z]+(/.+\.?/?)*$','https://tse4-mm.cn.bing.net/th/id/OIP.hZVc6Fj_F3vKxA66_ILksQDKEs?pid=Api&rs=1').group(0)
#match
<re.Match object; span=(0, 77), match='https://tse4-mm.cn.bing.net/th/id/OIP.hZVc6Fj_F3v>
#findall
('bing.', '/th/id/OIP.hZVc6Fj_F3vKxA66_ILksQDKEs?pid=Api&rs=1')

这里无论是直接用match还是用findall,正则表达式是通过的,但是显示效果不对

并未完全知道网址规则,仅以上来看,网址域名需要字母(包括中文)数字下划线破折号.后缀(英文),再后面就很花样了,基本与文件名规则一样,虽然我并未知晓,但包括了更多的字符类型

书上写的^\s,迷,我尝试的规则是在edge浏览器挨个排查的,域名不能包括括号等,至于后缀之后是否能包括空格,貌似可以,不过会被解析为%20

# %%timeit
def 返回key(d,v):for i in d:for j in d[i]:if j==v:return iimport re
import tkinter
s="""移动:134、135、136、137、138、139、147、150、151、152、157、158、159、182、183、187、188
联通:130、131、132、156、155、186、185、145(数据卡)、176
电信:133、153、189、180、181、177"""
运营商_前三位=dict()
for i in s.split("\n"):运营商_前三位[i[0:2]]=re.findall('\d+',i)
(l前三位:=[s for i in list(运营商_前三位.values()) for s in i]).sort()
for i in l前三位:
#     返回key(运营商_前三位,i)print(i,返回key(运营商_前三位,i))

测试的话,这个是最快也最简单的,46µs
我本想用这个生成那啥,前三位的正则表达式来着,但是觉得能复习下之前学的东西,接下来用pandas处理下!看看哪个快!

突然想起以前的一个排序函数是有key的,记录下也尝试写一下!

%%timeit
import re
s="""移动:134、135、136、137、138、139、147、150、151、152、157、158、159、182、183、187、188
联通:130、131、132、156、155、186、185、145(数据卡)、176
电信:133、153、189、180、181、177"""
运首=dict()
for i in s.split("\n"):运=i[0:2]for j in re.findall('\d+',i):运首[j]=运
for i in sorted(运首):运首[i]

19.3 µs ± 420 ns

还是没有写sort key的机会,以前是想着整整齐齐,再反过来,那如果没有必须的话,不如一开始就是反过来的。

那还是继续试一下pandas吧!

import re
import pandas
s="""移动:134、135、136、137、138、139、147、150、151、152、157、158、159、182、183、187、188
联通:130、131、132、156、155、186、185、145(数据卡)、176
电信:133、153、189、180、181、177"""
运首=dict()
for i in s.split("\n"):运=i[0:2]for j in re.findall('\d+',i):运首[j]=运
p=pandas.DataFrame(运首,index=[0]).T
print(p.sort_index())
#0
130  联通
131  联通
132  联通
133  电信
134  移动
135  移动
136  移动
137  移动
138  移动
139  移动
145  联通
147  移动
150  移动
151  移动
152  移动
153  电信
155  联通
156  联通
157  移动
158  移动
159  移动
176  联通
177  电信
180  电信
181  电信
182  移动
183  移动
185  联通
186  联通
187  移动
188  移动
189  电信

2.69 ms - 2.45 ms
而排序仅一步都大大超过亲手去写的个零头

暂时不知道如何不显示第一行,但是这个效率说实话真不高,只适合局域网服务器,或者单机,不适合web服务器

几乎回来,尝试生成正则表达式,其实最简单的就是一串|,不过我的目标是和书中的一样2+1的模式。最后对比下效率!

s="""移动:134、135、136、137、138、139、147、150、151、152、157、158、159、182、183、187、188
联通:130、131、132、156、155、186、185、145(数据卡)、176
电信:133、153、189、180、181、177"""
(l:=re.findall('\d+',s)).sort()
d={}
c=l[0][0:2]
t=[l[0][2]]
for i in l[1:]:if i[0:2]==c:t.append(i[2])else:d[c]=t;c=i[0:2];t=[i[2]]
d[c]=t
s=''
for i in d:s+=i+"["+(t:=d[i][0])c=t=int(t)for j in d[i][1:]:if (ij:=int(j))-t==1:if s[-1]!="-":s+="-";t=ijelse:t=ijelif s[-1]=="-":if t==c:s=s[:-1];s+=str(t)+jelse:s+=str(t)+jc=t=int(j)else:s+=jif s[-1]=="-":if t==c:s=s[:-1];s+=str(t)else:s+=str(t)s+="]|"
s=s[:-1]
print(s)
#
13[0-9]|14[57]|15[0-35-9]|17[6-7]|18[0-35-9]

35.8 µs no print

re.match("^(13[0-9]|14[57]|15[0-35-9]|17[6-7]|18[0-35-9])\d{8}$",“13000000000”)
1.05 µs
18900000000
1.12 µs
re.match("^(130|131|132|133|134|135|136|137|138|139|145|147|150|151|152|153|155|156|157|158|159|176|177|180|181|182|183|185|186|187|188|189)\d{8}$",“13000000000”)
1 µs
18900000000
1.26 µs

于是,正则表达式1,对比次数5+8
正则表达式2,对比次数32次
于是还是缩写的好一点

至于中括号内要不要缩写
re.match("^(13[0123456789]|14[57]|15[012356789]|17[67]|18[012356789])\d{8}$",“13000000000”)
1.09 µs
18900000000
998 ns/1.04 µs
至少并没显示出比括号内缩写的差,可能是缩写还需要解码

唯一确定的是完全不缩写的未分组效率是最低的

使用正则表达式校验输入是否只包含2位小数的正实数

import re
i=input("输入一个带2位小数的正实数:")
# re.findall("\+?\d*\.\d{2}$","12.12")
if re.findall("^\+?\d*\.\d{2}$",i):f=float(i)print("输入正确:",f)
else:print("输入错误")
#
输入一个带2位小数的正实数:11.11
输入正确: 11.11

与书中的代码,主要区别就是书中又一次用了re.compile,于是我查了下,写在compile最开始出现的时候!

输入的是否全是汉字

9fa5 龥 在微软输入法中有,但是讯飞拼音没能找到,讯飞手写写出来了
9fa6 龦 两者都没找到,个人认为,使用全部的汉字范围比较合理
http://www.unicode.org/charts/PDF/U4E00.pdf
算了,就先按书上的吧!

import re
c=re.compile("^[\u4e00-\u9fa5]+$")
i=input("请只输入汉字:")
if c.match(i):print("输入正确")
else:print("输入错误")

这次与书上的差异,^[\u4e00-\u9fa5]{0,}$

使用正则表达式校验IP地址是否格式正确

import re
i=input("输入IP地址:")
if re.match('^\d{3}\.\d{3}\.\d{3}\.\d{3}$',i):print("输入正确")
else:print("输入错误")

典型案例

提取并汇总字符串中的费用

import re
r=re.compile("\d+(?:\.01)?")
l=r.findall("本月消费:吃饭3元,睡觉60元,打豆豆0.01元")
s=0
for i in l:s+=float(i)
print("本月消费合计:",s)

这里遇到的问题,第一次是\d+(.\d+)?,但是结果不对劲,只显示匹配到的括号的数值,这个问题之前也发生过,那时没有深究。
零这个表达式下,search和findall的结果也不一样

结果,是()有双重意思:
1、()内部是一个整体
2、()是扩展语法
所以要单纯把他当做一个整体来判断,需要在内部添加?:

校验字符串的标示符合法性

不以数字开头的中英文数字下划线长度大于1

[A-Z_a-z\u4e00-\u9fa5][0-9A-Z_a-z\u4e00-\u9fa5]*
(?!\d)(^[0-9A-Z_a-z\u4e00-\u9fa5]+$)
import re
i=input("输入符合标示符规则的字符串:")
if re.match("(?!\d)(^[0-9A-Z_a-z\u4e00-\u9fa5]+$)",i):print("输入正确")
else:print("输入错误")

校验密码规格

import re
i=input("校验密码,6~20位,数字、英文、下划线,不能纯数字:")
if re.match("(?!^\d*$)(^[0-9A-Z_a-z]{6,20}$)",i):print("输入正确")
else:print("输入错误")

书中:^(?!\d+$)[\dA-Za-z_]{6,20}$
我只能说他让我看到了另一种方式,但是我不认同这种写作方式,但可以借鉴

校验QQ号

import re
i=input("QQ号,从10000开始,目前10位,将从输入的一串串的数字中辨识可能的QQ号码!\n请输入:")
if r:=re.findall("(?<!\d)[1-9]\d{4,9}(?!\d)",i):print("可能存在的QQ号:",r)
else:print("没有可能存在的QQ号")
#
QQ号,从10000开始,目前10位,将从输入的一串串的数字中辨识可能的QQ号码!
请输入:3219321,123,12389890432758974892375,10000
可能存在的QQ号: ['3219321', '10000']

这里算是第一次认真的用到了扩展,前面不能是数字,后面不能是数字。

不等同于(?<=\D)[1-9]\d{4,9}(?=\D)
?!\d是指不能是数字,可以是数字以外的或是没有
而?=\D,他的要求就是字符不能是数字,所以这里是要有字符的所以以上输入的话,结果是没有QQ号

解析网页内容:Urllib

在Python中有Urllib库
(1)Urllib.request:打开和读取URLs的内容
(2)Urilib.error:包含使用urlilib.request时可能产生的错误和异常
(3)Urllib.parse:解析和处理URLs
(4)Urllib.roborparse:解析robots.txt文件

使用正则表达式模块和网页抓取模块解析网页内容

(1)在浏览器中输入网址http:\www.kugou.com打开酷狗音乐网站
(2)右键单击,选择“查看网页源代码”菜单项分析“酷狗音乐”网站主页源代码
(3)编写相应的正则表达式
(4)使用证则表达式对网站内容进行提取

import urllib.request,re
with urllib.request.urlopen('http://www.kugou.com/') as file:#网页状态print("网页状态:",file.status,file.reason)data=file.read().decode('utf-8')#标题reg='<title>(.*?)</title>'title=re.findall(reg,data,re.S|re.M)print("标题:",title)reg='<meta name="keywords" content=.*?>'keywords=re.findall(reg,data,re.S|re.M)keywordsList=[]for item in keywords:keywordsList=re.findall('content="(.*?)" />',item)print("关键字:",keywordsList)#客户端程序下载类型reg='<div class="download">(.*?)</div>'download=re.findall(reg,data,re.S|re.M)for item in download:downloadList=re.findall("<a .*?>(.*?)</a>",item)print("客户端程序下载类型:",downloadList)#顶级导航reg='<ul class="bsUl">(.*?)</ul>'top_nav=re.findall(reg,data,re.S|re.M)top_nav_list=[]for item in top_nav:top_nav_list=re.findall("<a .*?>(.*?)</a>",item)
#     top_nav_list=re.findall("<a .*?>(.*?)</a>",top_nav[0])print("顶级导航:",top_nav_list)#主页导航reg='<ul class="homeNav">(.*?)</ul>'home_nav=re.findall(reg,data,re.S|re.M)home_nav_list=[]for item in home_nav:home_nav_list=re.findall("<a .*?>(.*?)</a>",item)print("主页导航:",home_nav_list)#首页模块reg='<h3><b>(.*?)</h3>'secound_content=re.findall(reg,data,re.S|re.M)secound_content_list=[]for item in secound_content:secound_content_list.append(re.sub("<.*?>","",item))print("首页模块:",secound_content_list)
#
网页状态: 200 OK
标题: ['酷狗音乐 - 就是歌多']
关键字: ['酷狗音乐旗下最新最全的在线正版音乐网站,本站为您免费提供最全的在线音乐试听下载,以及全球海量电台和MV播放服务、最新音乐播放器下载。酷狗音乐 和音乐在一起。']
客户端程序下载类型: ['下载PC版', '下载iPhone版', '下载Android版']
顶级导航: ['客服中心', '招贤纳士', '会员中心 ', '商务合作 ']
主页导航: ['首页', '榜单', '下载客户端', '更多']
首页模块: ['精选歌单', '热门榜单', '新歌首发', '推荐MV', '热门电台', '热门歌手', '合作伙伴', '友情链接']

在程序运行时,如果待解析的网站地址不正确或网络不连通,则出现URLError: <urlopen error [Errno 11001] getaddrinfo failed>等错误。
如果网站出现较大的变化,则需重新对网站的源代码进行分析并编写对应的正则表达式,否则可能得不到正确的结果。

进阶实例

Python 正则表达式同时删除多种匹配
在此实例中,能够获知正则表达式的处理方式,即:字符串是流程,触发规则会跳过相关字符串,而被跳过的字符串中,存在的触发规则就无法被触发!

Python 转义字符\N{…} Unicode全支持代码

主要是括号的坑

    l=re.findall('(?<=%%timeit\r\n   ...: ).*(?=\r\n)|.*(?= ± \d)',s)#['1>2 and 2>3','54.8 ns','']l=re.findall('(?<=%%timeit\r\n   ...: ).*(?=\r\n)|(?<=\n).*(?= ± \d)',s)#['1>2 and 2>3','54.8 ns']

零宽断言

匹配包括回车的字符串

%%timeit
x=-1
x.__abs__()114 ns ± 0.347 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)s
Out[12]: '%%timeit\r\nx=-1\r\nx.__abs__()\r\n\r\n\r\n114 ns ± 0.347 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)'re.findall('(?<=%%timeit\r\n)[^\ud800]*?(?=\r\n\r\n)',s)
Out[20]: ['x=-1\r\nx.__abs__()']
re.findall('(?<=%%timeit\r\n)[\x00-\ud7ff]*(?=\r\n\r)',s)
Out[15]: ['x=-1\r\nx.__abs__()\r\n']

这里要匹配的是自%%timeit后的语句块,因为期间有回车,于是尝试寻找解决方法,如果回车数量一定,可以使用.*/s*的方法,但是语句行数并不一定,而[ ]的方法无法使用. /s等转义字符,只能是范围,所以于是暴力的使用unicode范围去指代,注意block中,三个分区不能使用,需要绕过他们,这里我懒得写后面的部分,不确定的话,可以自己加上去。例如我一开始使用[\x00-\U00110000]其实也是错误写法,至少是[\x00-\U0010FFFF],我试了下

re.findall('(?<=%%timeit\r\n)[\U00000000-\U0010FFFF]*(?=\r\n\r)',s)
Out[19]: ['x=-1\r\nx.__abs__()\r\n']

完全没问题,于是确定是之前00110000的写法触发了错误,总之,大可以这样写,而不能[.\s]*

re.findall('(?<=%%timeit\r\n)[^\ud800]*?(?=\r\n\r)',s)
Out[20]: ['x=-1\r\nx.__abs__()']

这样写也是没问题的,上面提到,\ud800是个私用区

regex替代re

http://www.langmead-lab.org/teaching-materials/pdf=re.findall(r'http[^"]*pdf',s)
re.findall('pdf"[^>]*>(.*?)<',s)
# 这样写没问题l=re.findall('(?<=/)[^/]*(?=.pdf)|(?<=pdf"[^>]*>).*?(?=<)',s)
# 想同时获得两个数据,就有问题re.findall('(?<=pdf"[^>]*>).*?(?=<)',s)
error: look-behind requires fixed-width pattern
re.findall(r'(?<=pdf"[^>]*{30,40}>)(.*?)<',s)
error: multiple repeat

接上,同时获得pdf文件名和edge翻译的文件名,上面第二项的前面有问题

l=re.findall('/([^/]*).pd|f"[^>]*>(.*?)<',s)

临时解决方案,结果是元组

多出查找得出的结论是,python使用的规则不支持变长,只支持定长例如{10}、a|b,不支持*、{10,11}、a|ab

或者:安装 regex 模块替代原有的 re

regex.findall('(?<=/)[^/]*(?=.pdf)|(?<=pdf"[^>]*>).*?(?=<)',s)

漂亮,虽然括号的问题仍在,但至少这个问题解决了

python - 啃书 第八章 正则表达式相关推荐

  1. python - 啃书 第七章 模块、包和库 (二)

    常用第三方库 Pandas Pandas 中文网 Pandas是基于NumPy库的一种解决数据分析任务的工具库 Pandas库纳入了大量模块和一些标准的数据模型,提供了高效的操作大型数据集所需的工具 ...

  2. python - 啃书 第五章 函数

    函数定义和调用 函数定义 函数是:组织好的,可重复使用的,用来实现单一或相关联功能的代码段. 在程序中,函数的使用能提高应用的模块性.代码的重用率和可读性. 自定义函数的一般格式为: def 函数名( ...

  3. python - 啃书 第七章 模块、包和库 (一)

    概述 在Python中,一个模块(Module)是一个以.py结尾的Python文件,包含了Python对象和语句. 在python中,一切皆对象.数字.字符串.元组.列表.字典.函数.方法.类.模块 ...

  4. python - 啃书 第十二章 图形用户界面编程

    此篇中,介绍的是个第三方库,而该库的书籍,Creating GUI Applications with wxPython Kindle Edition, 358 pages,完全是把一个前端教程缩减到 ...

  5. python - 啃书 第十一章 数据库访问

    概述 与使用文件存储数据相比,使用数据库存储和管理数据更容易实现数据共享.降低数据冗余.保持数据独立性,以及增强数据的一致性和可维护性. 现在数据库技术已经广泛应用于电子邮件.金融业.网站.办公自动化 ...

  6. python - 啃书 第九章 文件访问

    概述 计算机文件是存储在外部存储器上的数据集合.通常计算机处理的大量数据都是以文件的形式组织存放的,操作系统也是以文件为单位对数据进行管理的. 每个文件都有一个文件名,文件名由基本名和扩展名组成,不同 ...

  7. python编程基础与应用-有哪些适合零编程基础的人学习Python的书?

    筛选了2年内优秀的python书籍,个别经典的书籍扩展到5年内. python现在的主流版本是3.7(有明显性能提升,强烈推荐) 3.6, 不基于这两个或者更新版本的书,慎重选择.很多库已经不提供py ...

  8. 适合自学python的图书-有哪些适合零编程基础的人学习Python的书?

    很多人在问,学习Python读什么书,这其实是一个非常通用的问题,学习分为三种方式:看书.上课.培训,而读书学习是最实惠也是最高效的一种,下面我们推荐6本高分书籍给大家,希望大家学习愉快: 1.Pyt ...

  9. python动物书_图灵出品的人气O'Reilly动物书,你更钟意哪本?

    今天七夕,咱们图灵教育的微信又赠书了,两拨哦~ 送<计算机程序设计的艺术>(TAOCP)(含卷1和卷2,中英文版任选)了,活动截止到10号零点,点击链接赶紧去微信留言吧. 送挑3个人送书单 ...

  10. Python之Re模块匹配正则表达式详解

    目录 前言 1. re.match方法 2. re.search方法 3. 检索和替换方法re.sub 4. re.compile方法 5. re.findall方法 6. re.finditer方法 ...

最新文章

  1. Exchange 2013恢复已禁用用户邮箱
  2. CentOS7系统 yum 安装报错
  3. VTK:相互作用之Picking
  4. hasOwnProperty
  5. 自注意力机制Self-attention(1)
  6. linux 目录结构+常用命令+压缩命令+vim使用+及一些基础知识(非常好)
  7. 计算机编程pdf百度云,计算机编程基础.pdf
  8. 密码编码学之AES及其工作模式详解
  9. mysql解压包安装出现 No such file or directory错误的解决办法
  10. 相关登录随机验证码公共函数
  11. Codeforces Round #499 (Div. 2): F. Mars rover(DFS)
  12. 15个Java的报表工具简介
  13. java使用谷歌验证码google captcha
  14. Win10环境下 Cad插件使用失败 解决方法
  15. matlab画概率密度图
  16. 网易微专业python全栈工程师_Python学习笔记:6.3.10 flash WTF数据验证,6310flaskwtf
  17. 青龙面板 企业微信应用推送
  18. 分组折线图、柱状图实现(多条折线图、柱状图同时显示)实现方式
  19. Python3.6实现图片转文字
  20. Windows上值得一用的软件

热门文章

  1. Android WebView 图片加载不出来
  2. 解决win8无法使用内置管理员账户打开
  3. java语言中modifiers_Java基础——Modifier类
  4. Nature封面:基因突变才是衰老的罪魁祸首?体细胞突变越快,寿命越短
  5. 蓄力一纪,可以远矣!十二年的百度地图和他的AI新征程
  6. 如何在Mac OS X上创建一个Service服务进程
  7. 进军欧罗巴:中国区块链企业何以敲开欧洲市场的大门
  8. linux中opt是啥文件夹,linux根目录的各文件夹里装了什么
  9. 2003服务器安全策略
  10. SpringBoot父子工程集成Jenkins部署项目问题合集