Python正则表达式(入门到进阶)

Python->re

之前想着玩爬虫,特意找一些大佬的文章学的,使用jupyter notebook写的,以下是导出成markdown语法形成的样子,一些更细节的解释,可以参看文末大佬总结的文章。

import re
str1 = "<table><tr>hello world 18111234589<tr><tr><span>name:张三,tel:18711001111</span></tr></table>"

'''
需求:
1.提取字符串中<span>标签里的内容;
2.提取其中所有的手机号;
'''
#第一题:
start = str1.find('<span>')
end = str1.rfind('</span>')

if start != -1:
    print(str1[start+len('<span>'):end])

#第一题 获取<span>标签内的所有数据
info = re.search(r'(?<=<span>).*(?=</span>)',str1)
print(info.group(0))

#输出:name:张三,tel:18711001111

#第二题:
'''
提取手机号:(必须先清楚手机号的规则)
    1.必须是11位的数字
    2.第一位数字以1开头,第二位数字可以是[3,4,5,7,8]中的任意一个后面9个是[0-9]中的任意一个数字
    (现在规则更多了,出现了19**的号码,这里作为例子就简单一点);
'''

#第二题 提取所有手机号:
phone = re.findall(r'1[345678]\d{9}',str1)
print(phone)

#输出:['18111234589', '18711001111']
name:张三,tel:18711001111
name:张三,tel:18711001111
['18111234589', '18711001111']
'''
使用正则表达式有两个常见的原因:
    第一个原因是数据挖掘——也就是说,当我们希望在一大堆文本中找到一小堆文本,例如:身份证,手机号,e-mail等;

    第二个原因是验证,可以使用正则表达式确认获得的数据是你所期望的,如用户验证,密码长度和格式的验证;
re 模块
'''

#首先我们学习第一个函数,search()函数,它的目的是接收一个正则表达式和一个字符串,并返回发现的第一个匹配的字符串

a = re.search(r'fox','the quick brown fox jumpred')
#第一个参数为正则表达式,第二个参数为要处理的字符串
print(a.span()) #span方法获取的是正则表达式匹配到的位置,输出'(16,19)'

b = re.search(r'www','the quick brown fox jumpred')
print(b) #输出None
(16, 19)
None
#找到多个匹配

'''
re.search 的一个限制是它仅仅返回最近一个以 match 对象形式的匹配,如果在一个字符串内存在多个匹配,
re.search()只会返回第一个匹配,一般情况下这也是我们期望的结果,但是,有时候需要当多个匹配存在时进行多个匹配。

如果有这种需求咱们可以使用 findall 或者 finditer。
两个方法的区别在于 findall 返回的是一个列表,
finditer返回的是一个生成器
'''
l = re.findall(r'张','张三 张三丰 张无忌 张小凡')
print(l)

#在这个例子中,我们会发现findall返回了所有匹配的值,这是因为“张”字在后面的字符串中出现了4次
['张', '张', '张', '张']
#基础正则表达式
'''
最简单的正则表达式是哪些仅包含简单字母数字字符的表达式——不包含任何其他字符。
字符串Python是一个有效的正则表达式,它仅匹配单词,默认正则表达式区分大小写,例如:
'''
a = re.search(r'Python','I Like python')
print(a)
b = re.search(r'Python','I LIKE PYTHON')
print(b)
'''
不过如果只是使用正则表达式来匹配文本,没有什么实际的意义,
毕竟检测一个文本中是否有另一个字符串本来就非常简单。

正则表达式强大的地方在于能够指定用于匹配的文本模式。
'''
None
None
a = re.findall(r'[Pp]ython','I Like Python3 and I like python2.7')
print(a)

#可以发现[Pp]既可以匹配大写的P也可以匹配小写的p
#执行会发现输出为空数组,这里值的我们注意的是[Pp]仅匹配一个字符
['Python', 'python']
#区间
'''
有一些常见的字符组非常大,比如,我们要匹配的是任意数字,如果依照上述代码,
每次我们都需要使用[0123456789] 这种方式明显很不明智,
而如果要匹配从a-z的字母,我们也这样编写代码的话,肯定会让我们崩溃

为了适应这一点,正则表达式引擎在字符组中使用连字符(-)代表区间,
所以我们匹配任意数字可以使用[0-9],所以如果我们想要匹配所有小写字母,
可以写成[a-z],想要匹配所有大写字母可以写成[A-Z]

可能我们还有个需求:匹配连字符。因为-在会被正则表达式引擎理解为代表连接区间,
所以这个时候我们需要对-进行转义
'''
a = re.findall(r'[0-9]','xxx007abc')
b = re.findall(r'[a-z]','abc001ABC')
c = re.findall(r'[A-Za-z0-9]','abc007ABC')
d = re.findall(r'[0-9\-]','0edu 007-edu')
print(a)
print(b)
print(c)
print(d)
['0', '0', '7']
['a', 'b', 'c']
['a', 'b', 'c', '0', '0', '7', 'A', 'B', 'C']
['0', '0', '0', '7', '-']
#取反
a = re.findall(r'[^0-9]','xxx007abc')
b = re.search(r'[^0-9]','xxx007abc')
print(a)
print(b)
#可以通过在字符数组开头使用^字符实现取反操作,从而可以反转一个字符组(意味着会匹配任何指定字符之外的所有字符)
'''
接下来在看一个表达式:n[^e] 这意味着字符n接下来的字符是除了e之外所有的字符
'''
a = re.findall(r'n[^e]','final')
b = re.search(r'n[^e]','final')
c = re.findall(r'[n[^e]]','Python')
print(a)
print(b)
print(c)

'''
这里我们可以发现a和b匹配的是na,字符a因为不是e所以可以被匹配,
而变量c的值为空,在这里正则表达式引擎只匹配到了字符串n的位置,
而n之后没有任何可以匹配[^e]的字符了,所以这里也匹配失败
'''
['x', 'x', 'x', 'a', 'b', 'c']
<re.Match object; span=(0, 1), match='x'>
['na']
<re.Match object; span=(2, 4), match='na'>
[]
#快捷方式
'''
几种普通字符组还在正则表达式引擎中有几个预定义的快捷方式,如果我们想要定义单词,以目前学到的可能会使用[A-Za-z],但是,很多单词都是使用该巨剑以外的字符。比如中文,以及其他语言,

正则表达式引擎提供了一些快捷方式:w ,与 “任意单词字符”匹配,在Python3中,基本上可以匹配任何语言的任意单词。

而当我们想要匹配任意数字的时候也可以使用快捷方式 \d d即digit,在Python3中它除了可以和[0-9]匹配,还可以和其他语言的数字匹配。

\s快捷方式匹配空白字符,比如空格,tab、换行 等。

\b 快捷方式匹配一个长度为0的字符串,但是,他仅仅在一个单词开始或结尾处匹配,这被称为词边界快捷方式


快捷方式    描述
w    与任意单词匹配
d    与任意数字匹配
s    匹配空白字符,比如空格 tab 换行 等
b    匹配一个长度为0的子串  #空字符
'''

#示例:
a = re.findall(r'\w','学好Python 大展拳脚') #任意单词匹配
b = re.search(r'\w','python3') 
c = re.search(r'\d','编号89757')
print(a)
print(b)
print(c)
#这里findall会返回所有能匹配的值,search只会返回第一个匹配到的值

d = re.findall(r'\bmaster\b','masterxiao-master-xxx master abc')
e = re.search(r'\smaster\s','masterxiao master xxxx')
print(d)
print(e)
['学', '好', 'P', 'y', 't', 'h', 'o', 'n', '大', '展', '拳', '脚']
<re.Match object; span=(0, 1), match='p'>
<re.Match object; span=(2, 3), match='8'>
['master', 'master']
<re.Match object; span=(10, 18), match=' master '>
#快捷方式取反
'''
之前提到了取反,快捷方式也可以取反, 
例如对于 \w的取反为\W,可以发现将小写改写成大写即可。

注意这里\B 有所不同, b 匹配的是在单词开始或结束位置长度为0的子字符串,
而\B匹配不在单词开始和结束位置的长度为0的子字符串

'''
a = re.findall(r'\Bmaster\B','masterxiao master xxx master abc')

b = re.search(r'master\B','masterxiao')

c = re.findall(r'\Bmaster\B','1master1')
print(a)
print(b)
print(c)
[]
<re.Match object; span=(0, 6), match='master'>
['master']
#字符串的开始和结束

a = re.search(r'^python','this code in python3')
b = re.search(r'python$','this code in python3')

c = re.search(r'^python','python is my favorite')
d = re.search(r'python$','this is code in python')

print(a)
print(b)
print(c)
print(d)

#通过上述例子,我们可以发现 ^指定的是一个字符串的开始,$指定的是一个字符串的结束
None
None
<re.Match object; span=(0, 6), match='python'>
<re.Match object; span=(16, 22), match='python'>
#任意字符
import re
'''
.字符最后一个快捷方式字符,它代表匹配任何单个字符,不过值得注意的是,它只能出现在方括号字符组以外

值得注意的是:<font color = "red">.字符只有一个不能匹配的字符,
也就是换行(\n),</font>,不过让.字符与换行符匹配也是可能的,以后会讨论
'''
a = re.search(r'p.th.n','hello python re')
b = re.search(r'p.....','学好 python 人见人爱')
print(a)
print(b)
<re.Match object; span=(6, 12), match='python'>
<re.Match object; span=(3, 9), match='python'>
# 可选字符
'''
到目前为止,我们看到的正则表达式都是在正则表达式中的字符与被搜索的字符串中的字符保持1:1的关系。
不过有时,我们可能想要匹配一个单词的不同写法,比如“color”和“colour”,或者“honor”与“honour”。
这个时候我们可以使用 ? 符号指定一个字符、字符组或其他基本单元可选,这意味着正则表达式引擎将会期望该字符出现零次或一次
'''
a = re.search(r'honou?r','He Served with honor and distinction')
b = re.search(r'honou?r','He Served with honour and distinction')
c = re.search(r'honou?r','He Served with honou and distinction')

print(a)
print(b)
print(c)

'''
可以发现,在上述三个例子中,正则表达式为honou?r,这里可以匹配的是 honor 和 honour 不能匹配 honou,
可以知道的是 ? 确定了前一个u是可选的,在第一个示例中,没有u,是没有问题可以匹配的,
在第二个示例中,u存在这也没有问题。在第三个例子中,u存在但是r不存在,这样就不能匹配了

'''
<re.Match object; span=(15, 20), match='honor'>
<re.Match object; span=(15, 21), match='honour'>
None
# 重复
'''
到目前为止,我们只是学习了关于仅出现一次的字符串匹配,在实际开发过程中,这样肯定不能满足需求,比如要匹配电话号码,比如匹配身份证号,这些都是很多个数字组成的。

如果遇到这样的情况,我们可能期望一个字符组连续匹配好几次。

在正则表达式在一个字符组后加上{N} 就可以表示 {N} 之前的字符组出现N次
'''
re.findall(r'[\d]{4}-[\d]{7}','张三:0731-8825951,李四:0733-8794561')
['0731-8825951', '0733-8794561']
#重复区间
'''
可能有时候,我们不知道具体要匹配字符组要重复的次数,比如身份证有15位也有18位的。

这里重复区间就可以出场了,语法:{M,N},M是下界而N是上界
举个例子:
'''
a = re.search(r'[\d]{3,4}','0731')
b = re.search(r'[\d]{3,4}','073')
print(a)
print(b)

'''
通过上述代码,我们发现[\d]{3,4} 既可以匹配3个数字也可以匹配4个数字,
不过当有4个数字的时候,优先匹配的是4个数字,这是因为正则表达式默认是贪婪模式,
即尽可能的匹配更多字符,而要使用非贪婪模式,我们要在表达式后面加上 ?号

'''
d = re.search(r'[\d]{3,4}?','0734')
e = re.search(r'[\d]{2,3}?','073')
print(d)
print(e)
<re.Match object; span=(0, 4), match='0731'>
<re.Match object; span=(0, 3), match='073'>
<re.Match object; span=(0, 3), match='073'>
<re.Match object; span=(0, 2), match='07'>
#开闭区间
#有时候我们可能遇到字符组的重复次数没有边界:
#闭区间不写即可表示匹配一个或多个。
a = re.search(r'[\d]{1,}','0731 xxx')
print(a)
<re.Match object; span=(0, 4), match='0731'>
#速写
#还可以使用两个速写字符指定常见的重复情况,
#可以使用 + 匹配1个或多个,
#使用 *代表0个或多个
a = re.findall(r'[\d]+','0731-8859456')
b = re.findall(r'[\d]*','编号89758')
print(a)
print(b)
['0731', '8859456']
['', '', '89758', '']
#分组
'''
在Python正则中还提供了一种机制将表达式——分组,当使用分组时,
除了获得整个匹配。还能够在匹配中选择每一个分组。
要实现分组很简单,使用()即可。
'''
a = re.search(r'([\d]{4})-([\d]{7})','张三:0731-8825951') #未使用分组
print(a.group())
print(a.groups())
print(a.group(1))
print(a.group(2))
0731-8825951
('0731', '8825951')
0731
8825951
#练习 识别中国电话号码
#中国区号码规则如下: (+)86-0XX(0XXX)-XXX-XXXX(XXXX-XXXX)
phone='''
+86-010-82866931
86010-82866931
010 8286 6931
86(010)-69675217
86(0731)-728-3333
(0731)-2728-3353
'''
#请你使用正则表达式将这些号码提取成分组形式
#命名分组
#一个命名分组的语法是在开始的“(”之后,添加?P<group_name>,来实现分组,例如:
import re
a = re.search(r'[\d]{4}-[\d]{7}','张三:0731-8825951')
b = re.search(r'(?P<first_group>[\d]{4})-(?P<second_group>[\d]{7})','张三:0731-8825951')
print(a)
print(a.group)
print(a.groups)
print(" -"*30)

print(b)
print(b.group())
print(b.groups())
print(b.group(0))
print(b.group(1))
print(b.group(2))
print(" -"*30)

print(b.group('first_group'))
print(b.group('second_group'))
print(b.groupdict())

'''
re.search()方法返回的是一个re.Match对象,运行上述例子我们可以发现,使用 ?P<group_name> 可以实现分组, 
使用 .group(group_name)将分组名传入group()函数可以获取对应名称分组的数据,同样使用索引 也是可以获取对应分组的数据的。
Match对象还提供了一个groupdict()方法,该方法在和groups()方法类似,不过它返回的是一个字典,而groups()放回的是一个元祖。
不过值得注意的是:groupdict()只会返回命名分组而不会返回非命名分组

'''

match = re.search(r'(?P<first_group>[\d]{4})-([\d]{7})','张三:0731-8825951') #使用分组
print(match.group())
print(match.groups())
print(match.groupdict())

"""
在上述例子中,只有第一个是命名分组,第二个是编码分组,所以当调用groups()时,
这两个分组都在元祖中返回,但是,当调用groupdict时,这两个分组则只有命名分组被返回了,
如果从可维护的角度来说,命名分组十分有价值

"""
<re.Match object; span=(3, 15), match='0731-8825951'>
<built-in method group of re.Match object at 0x0000016EDCFF0B90>
<built-in method groups of re.Match object at 0x0000016EDCFF0B90>
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
<re.Match object; span=(3, 15), match='0731-8825951'>
0731-8825951
('0731', '8825951')
0731-8825951
0731
8825951
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0731
8825951
{'first_group': '0731', 'second_group': '8825951'}
0731-8825951
('0731', '8825951')
{'first_group': '0731'}
'\n在上述例子中,只有第一个是命名分组,第二个是编码分组,所以当调用groups()时,\n这两个分组都在元祖中返回,但是,当调用groupdict时,这两个分组则只有命名分组被返回了,\n如果从可维护的角度来说,命名分组十分有价值\n\n'
# 引用已经存在的分组
'''
正则表达式还提供了一种引用一个之前匹配分组的机制,有些时候,我们或许会寻找到一个子匹配,该匹配会接下来再次出现。
例如,如果我们尝试解析一段XML代码,比如:<font>提示</font>,我们可能会编写出这样一段代码:
'''
a = re.search(r'<[\w_-]+>提示</[\w_-]+>','<font>提示</font>')
print(a)

#上述代码确实可以匹配,不过也存在另一种情况,如果解析的是如下数据:<font>提示</bar>
b = re.search(r'<([\w_-]+)>提示<(/[\w_-]+)>','<font>提示</bar>')
print(b)
print(b.group(1))
print(b.group(2))

print(" -"*30)


#这个时候我们可能直觉的想让后面分组的正则也匹配font,不过实际情况确是,
#所有形式的都会匹配,那如果我们想让后面的正则和第一个分组的正则该如何做呢?
#可以使用分组引用,使用\N即可回溯引用编号为N的分组,因此上述例子的代码我们可以改为:

a = re.search(r'<([\w_-]+)>提示</\1>','<font>提示</bar>')
b = re.search(r'<([\w_-]+)>提示</\1>','<font>提示</font>')
print(a)
print(b.group())
print(b.groups())
#通过上面的例子,我们可以发现回溯引用取代了第二个分组。所以与font不同的就无法匹配了;
#值的注意的是:这里只是一个作为介绍回溯引用的例子,
#在实际开发中我们不应该用这种方式去解析XML文档,可以使用beautifulSoap等包去解析XML文件
<re.Match object; span=(0, 15), match='<font>提示</font>'>
<re.Match object; span=(0, 14), match='<font>提示</bar>'>
font
/bar
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
None
<font>提示</font>
('font',)
#先行断言
a = re.findall(r'n(?!e)','final')
b = re.findall(r'n(?!e)','python')
print(a)
print(b)
print(" -" * 30)
#正向先行断言
c = re.findall(r'n(?=e)','final')
d = re.findall(r'n(?=e)','python')
e = re.findall(r'n(?=e)','jasmine')
print(c)
print(d)
print(e)
['n']
['n']
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[]
[]
['n']
#标记,不区分大小写
#最简单直接的标记是 re.IGNORECASE,他会导致正则表达式变为不区分大小写
a = re.search(r'python','I LIKE PYTHON',re.IGNORECASE)
b = re.search(r'python','I LIKE PYTHON',re.I)
print(a)
print(b)
print(" -" * 30)

#点匹配换行符
#re.DOTALL标记(别名为re.S)可以让 .字符除了匹配其他字符之外,还匹配换行符。
a = re.search(r'.+','hello\nworld')
b = re.search(r'.+','hello\nworld',re.S)
c = re.search(r'.+','hello\nworld',re.DOTALL)

print(a)
print(b)
print(c)
#详细模式
'''
re.VERBOSE标记(别名为re.X)允许复杂的正则表达式以更容易的方式表示
该标记做两件事,首先,他导致所有的空白(除了字符组中)被忽略,包括换行符。
其次,它将#字符(同样,除非在字符组内)当做注释字符
'''
#调试模式
#re.DEBUG 标记(没有别名)在编译正则表达式时将一些调试信息输出到sys.stderr
'''
使用多个标记
可能我们还需要同时使用多个标记,为了完成这点,可以使用|操作符。
正确的语法是例如:re.DOTALL|re.MULTILINE 或 re.S | re.M

'''
#内联标记
print(" -" * 30)
f = re.search('(?i)Foo','foo')
print(f)
#可以发现这里的(?i),就等同于使用re.IGNORECASE 标记。
<re.Match object; span=(7, 13), match='PYTHON'>
<re.Match object; span=(7, 13), match='PYTHON'>
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
<re.Match object; span=(0, 5), match='hello'>
<re.Match object; span=(0, 11), match='hello\nworld'>
<re.Match object; span=(0, 11), match='hello\nworld'>
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
<re.Match object; span=(0, 3), match='foo'>
#替换

'''
正则表达式引擎并不仅仅局限于识别一个模式是否在字符串中存在,它还能够执行字符串替换,基于在原始字符串中的分组返回一个新字符串。
Python中的替换方法是re.sub,他接收三个参数,正则表达式,用于替换的字符串,被搜索的字符串。
只要实际匹配被替换,如果没有匹配,则re.sub最终不执行任何操作。
re.sub允许从被替换的字符串中的正则表达式模式使用同样的回溯引用,接下来我们看一个从电话号码中剥离无关格式数据的任务
'''
a = re.sub(r'[\d]+',r'a','213-667-8890')
print(a)
# 还可以利用sub的功能使所有的电话号码格式一致
b = re.sub(r'(\+?1?)[ .-]?\(?([\d]{3})\)?[ .-]?([\d]{3})[ .-]?([\d]{4})',r'(\2)\3-\4','213-667-8890')

c = re.sub(r'(\+?1?)[ .-]?\(?([\d]{3})\)?[ .-]?([\d]{3})[ .-]?([\d]{4})',r'(\2)\3-\4','(213)8675509')

d = re.sub(r'(\+?1?)[ .-]?\(?([\d]{3})\)?[ .-]?([\d]{3})[ .-]?([\d]{4})',r'(\2)\3-\4','213.867.5509')

e = re.sub(r'(\+?1?)[ .-]?\(?([\d]{3})\)?[ .-]?([\d]{3})[ .-]?([\d]{4})',r'(\2)\3-\4','+1-213-854-5557')

f = re.sub(r'(\+?1?)[ .-]?\(?([\d]{3})\)?[ .-]?([\d]{3})[ .-]?([\d]{4})',r'(\2)\3-\4','1213-854-5557')
print(b)
print(c)
print(d)
print(e)
print(f)
a-a-a
(213)667-8890
(213)867-5509
(213)867-5509
(213)854-5557
(213)854-5557
#已编译的正则表达式
#re模块包含一个函数:compile,它返回一个已编译的正则表达式对象,该对象之后可以被复用
regex = re.compile(r'[\d]{3,4}')

a = regex.search('578')
b = regex.search('编号8975')

print(a)
print(b)
'''
使用已编译的正则表达式有两个好处:
    可用于作为方法的参数被传递
    允许使用在re.search允许使用在re.search中不可用的两个额外参数,
    这两个参数分别是被搜索字符串的开始和结束位置,他们可用来减少对部分字符串的考虑

'''

reg = re.compile('[\d]+')
c = reg.search('1 编号89757,wuhhhhh')
d = reg.search('1 编号89757,wuhhhhh',pos=2)
#pos:匹配的起始位置,可选,默认为0
#endpos:匹配的结束位置,可选,默认为 len(string)
print(c)
print(d)
<re.Match object; span=(0, 3), match='578'>
<re.Match object; span=(2, 6), match='8975'>
<re.Match object; span=(0, 1), match='1'>
<re.Match object; span=(4, 9), match='89757'>
#正则表达式练习
str='''
张伟 86-14870293148  \n
   王伟   +86-13285654569    \n
    王芳        15856529115    \n
 李伟         13022816340  \n
  王秀英   (86)14785720656     \n
   李秀英    17201444672    \n
    李娜         15682812452     \n
    张秀英         14326967740     \n
    刘伟  15146435743    \n
   张敏        (86)-17712576838   \n
    李静       86 14295083635  \n
    张丽     (+86) 13722348123   \n
   王静         17587918887   \n
  王丽    15493106739    \n
 李强      13786842977   \n
 张静         86-15542304386     \n
    李敏         15642387356 \n
   王敏          18627216756  \n
 王磊       17206185726   \n
    李军      17857426238     \n
   刘洋        17345352790     \n
'''
#要求:
# 1.提取所有 11 位数字电话号码

# 2.提取所有 18 或 13 开头的电话号码

# 3.提取所有“王”姓同学的名字

# 4.提取所有“张”姓同学的电话号码

# 5.重新排版,排版成统一的格式,去掉国家区号。

#1
a = re.findall(r'[\d]{11}',str)
#for i in a:
#    print(i)
print(a)
print(" -" * 30)
#2
b = re.findall(r'1[3|8][\d]{9}',str)
print(b)
print(" -" * 30)
#3
c = re.findall(r'王\S*',str)s
print(c)
print(" -" * 30)

#4
d = re.findall(r'(张\S*)\s+\(?(\+?86?)?\)?[ .-]?([\d]{11})',str)
for line in d:
    print(line[0]+"的电话是:"+line[2])
['14870293148', '13285654569', '15856529115', '13022816340', '14785720656', '17201444672', '15682812452', '14326967740', '15146435743', '17712576838', '14295083635', '13722348123', '17587918887', '15493106739', '13786842977', '15542304386', '15642387356', '18627216756', '17206185726', '17857426238', '17345352790']
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
['13285654569', '13022816340', '13722348123', '13786842977', '18627216756']
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
['王伟', '王芳', '王秀英', '王静', '王丽', '王敏', '王磊']
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
张伟的电话是:14870293148
张秀英的电话是:14326967740
张敏的电话是:17712576838
张丽的电话是:13722348123
张静的电话是:15542304386

参考:

本文链接:

https://www.betao.cn/archives/pythonre.html
1 + 1 =
快来做第一个评论的人吧~