Crossin的编程教室

标题: 【Python 第58课】 正则表达式(4) [打印本页]

作者: crossin先生    时间: 2013-9-23 20:10
标题: 【Python 第58课】 正则表达式(4)
1.
我们已经了解了正则表达式中的一些特殊符号,如\b、\d、.、\S等等。这些具有特殊意义的专用字符被称作“元字符”。常用的元字符还有:

\w - 匹配字母或数字或下划线或汉字(我试验下了,发现3.x版本可以匹配汉字,但2.x版本不可以)
\s - 匹配任意的空白符
^ - 匹配字符串的开始
$ - 匹配字符串的结束

2.
\S其实就是\s的反义,任意不是空白符的字符。同理,还有:

\W - 匹配任意不是字母,数字,下划线,汉字的字符
\D - 匹配任意非数字的字符
\B - 匹配不是单词开头或结束的位置

[a]的反义是[^a],表示除a以外的任意字符。[^abcd]就是除abcd以外的任意字符。

3.
之前我们用过*、+、{}来表示字符的重复。其他重复的方式还有:

? - 重复零次或一次
{n,} - 重复n次或更多次
{n,m} - 重复n到m次

正则表达式不只是用来从一大段文字中抓取信息,很多时候也被用来判断输入的文本是否符合规范,或进行分类。来点例子看看:
^\w{4,12}$
这个表示一段4到12位的字符,包括字母或数字或下划线或汉字,可以用来作为用户注册时检测用户名的规则。(但汉字在python2.x里面可能会有问题)

\d{15,18}
表示15到18位的数字,可以用来检测身份证号码

^1\d*x?
以1开头的一串数字,数字结尾有字母x,也可以没有。有的话就带上x。

另外再说一下之前提到的转义字符\。如果我们确实要匹配.或者*字符本身,而不是要它们所代表的元字符,那就需要用\.或\*。\本身也需要用\\。
比如"\d+\.\d+"可以匹配出123.456这样的结果。

留一道稍稍有难度的习题:
写一个正则表达式,能匹配出多种格式的电话号码,包括
(021)88776543
010-55667890
02584453362
0571 66345673

#==== Crossin的编程教室 ====#
微信ID:crossincode
论坛:http://crossin.me
QQ群:156630350

面向零基础初学者的编程课
每天5分钟,轻松学编程



作者: aresli    时间: 2013-9-24 17:26
\(?0\d{2,3}[) -]?\d{8}

挽尊!
作者: liqing215    时间: 2013-9-24 19:44
提问crossin先生:
      您的微信公众帐号后面是用编辑模式还是开发模式啊?
      用开发模式的话它自带的是用php写的,要是python的话不知道怎么改写,

作者: liqing215    时间: 2013-9-24 19:45
liqing215 发表于 2013-9-24 19:44
提问crossin先生:
      您的微信公众帐号后面是用编辑模式还是开发模式啊?
      用开发模式的话它自带 ...

学了一点点web.py, 是不是用Djingo会好些?
作者: liqing215    时间: 2013-9-24 19:54
aresli 发表于 2013-9-24 17:26
\(?0\d{2,3}[) -]?\d{8}

挽尊!

不是最后一种是空白符么。
作者: aresli    时间: 2013-9-25 10:16
liqing215 发表于 2013-9-24 19:54
不是最后一种是空白符么。

什么空白符?
作者: crossin先生    时间: 2013-9-25 12:41
liqing215 发表于 2013-9-24 19:44
提问crossin先生:
      您的微信公众帐号后面是用编辑模式还是开发模式啊?
      用开发模式的话它自带 ...

编辑模式

开发模式就不能用微信自带的功能了。
用python也可以,本质上就是收到一个xml文件,再返回一个xml文件
作者: fl0w    时间: 2013-9-25 16:00
r'[(]?0\d{2,3}[)]?[-]?[ ]?\d{8}'     #这样通过
r'[(]?0\d{2,3}[)-]?[ ]?\d{8}'           #这样通过
r'[(]?0\d{2,3}[)- ]?\d{8}'                 #这样通过
r'(?0\d{2,3}[)- ]?\d{8}'                    #这样通过
r'(?0\d{2,3}[)]?[-]?[ ]?\d{8}'      #这样通过

作者: liqing215    时间: 2013-9-25 23:08
crossin先生 发表于 2013-9-25 12:41
编辑模式

开发模式就不能用微信自带的功能了。

xml文件的排版是不是很麻烦啊?

因为1.我想每天发多条图文信息,直接用编辑模式很好用,但如果直接在代码里排版岂不是很麻烦?

2.我又想让它实现一些自动查询功能,比如翻译或者查天气、但是编辑模式能提供的功能又只有关键字自动回复。

3.两者不可兼得么。。。?
作者: liqing215    时间: 2013-9-25 23:10
aresli 发表于 2013-9-25 10:16
什么空白符?

就是这种0571 66345673
如何匹配。。。
作者: aresli    时间: 2013-9-25 23:34
liqing215 发表于 2013-9-25 23:10
就是这种0571 66345673
如何匹配。。。

[ ]   这样就可以匹配空格了
作者: crossin先生    时间: 2013-9-26 13:39
liqing215 发表于 2013-9-25 23:08
xml文件的排版是不是很麻烦啊?

因为1.我想每天发多条图文信息,直接用编辑模式很好用,但如果直接在代 ...

不可。用了开发模式,你就得忍受排版的痛苦。你可以自己再写一个后台,用来帮自己排版,这样写一次就可以重复用了。好像也有人开发过这种基于“开发模式”的后台,你搜搜看
作者: Myk_cc    时间: 2013-9-29 22:51
通过这次学习 我改进了一下匹配手机号的正则表达式(在图中)再次匹配了下同学留下的数据果然又新出现好几个电话号码暗自高兴~


H30ZBMUHEZ0~)44BBR9XPKV.jpg (7.67 KB, 下载次数: 420)

H30ZBMUHEZ0~)44BBR9XPKV.jpg


作者: crossin先生    时间: 2013-9-30 10:53
Myk_cc 发表于 2013-9-29 22:51
通过这次学习 我改进了一下匹配手机号的正则表达式(在图中)再次匹配了下同学留下的数据果然又新出现好几 ...

有个小问题,正则里如果需要表示字符而不是特殊符号,应该用\转义,而不是加引号。这里加了引号就只是表示'这个字符。
而且在[]里的'('是不用转义的,直接写(就可以了。写成\(也可以
作者: Myk_cc    时间: 2013-9-30 13:09
crossin先生 发表于 2013-9-30 10:53
有个小问题,正则里如果需要表示字符而不是特殊符号,应该用\转义,而不是加引号。这里加了引号就只是表 ...

谢谢,又涨姿势了~
作者: michael    时间: 2013-10-2 00:36
\(?0\d{2,3}[)-]?\d{7,8}
作者: blake    时间: 2013-11-2 17:11
本帖最后由 blake 于 2013-11-2 17:16 编辑

为什么8楼的第三种不能通过呢?
r'[(]?0\d{2,3}[)- ]?\d{8}'                 #这样不通过

[ )-]和[) -]能通过
作者: crossin先生    时间: 2013-11-3 21:11
blake 发表于 2013-11-2 17:11
为什么8楼的第三种不能通过呢?
[ )-]和[) -]能通过

我也觉得没问题
作者: 脑子有音乐    时间: 2014-7-3 15:20
m = "(021)88776543\n010-55667890\n02584453362\n0571 66345673"
n=re.findall(r"\S{4,5}[ ]?\d*",m)
print n
作者: toddlerya    时间: 2014-7-16 12:22
r"[(]?\d{1,4}[)]?[-]?\s?\d{8}"
为什么这样就不对了
r"^[(]?\d{1,4}[)]?[-]?\s?\d{8}$"
作者: crossin先生    时间: 2014-7-16 19:32
toddlerya 发表于 2014-7-16 12:22
r"[(]?\d{1,4}[)]?[-]?\s?\d{8}"
为什么这样就不对了
r"^[(]?\d{1,4}[)]?[-]?\s?\d{8}$"

这样就限定了必须是文本的开头和结尾。除非只有一个号码才能匹配
作者: toddlerya    时间: 2014-7-17 13:34
crossin先生 发表于 2014-7-16 19:32
这样就限定了必须是文本的开头和结尾。除非只有一个号码才能匹配

嗯,原来是文本的开头和结尾啊~谢谢先生
作者: toddlerya    时间: 2014-7-17 13:35
crossin先生 发表于 2014-7-16 19:32
这样就限定了必须是文本的开头和结尾。除非只有一个号码才能匹配

嗯,原来是文本的开头和结尾啊~谢谢先生
作者: 周末晒被子    时间: 2016-1-8 01:05
本帖最后由 周末晒被子 于 2016-1-8 02:23 编辑
fl0w 发表于 2013-9-25 16:00
r'[(]?0\d{2,3}[)]?[-]?[ ]?\d{8}'     #这样通过
r'[(]?0\d{2,3}[)-]?[ ]?\d{8}'           #这样通过
r'[ ...

厉害,我也要掌握这种摸索的思维。看着都爽。

第3条不通过,应该是因为空格符虽然能够放进 [] 之中,但是却不能够放在[]中3个特殊字符的的末尾位置,如17楼同学所说。

第5条不通过,应该是因为 ( 不能直接跟 ? ,应该把 ( 放进 [] 之中再跟 ? ,这是从第1条和第5条对比得出的想法。
作者: 周末晒被子    时间: 2016-1-8 01:54
脑子有音乐 发表于 2014-7-3 15:20
m = "(021)88776543\n010-55667890\n02584453362\n0571 66345673"
n=re.findall(r"\S{4,5}[ ]?\d*",m)
prin ...

哈哈,这种写法真任性~

但是使用场景比较局限。
作者: 周末晒被子    时间: 2016-1-8 02:05
本帖最后由 周末晒被子 于 2016-1-8 02:21 编辑
michael 发表于 2013-10-2 00:36
\(?0\d{2,3}[)-]?\d{7,8}

这个正则会读不出第4个号码,因为忘记加上空格符了。
作者: 周末晒被子    时间: 2016-1-8 02:19
最后放一下自己的版本

[(]?\d{3}[)-]?[\d]?[ ]?\d{7,8}
作者: catherinemic    时间: 2016-2-4 12:05
本帖最后由 catherinemic 于 2016-2-4 12:11 编辑

r'\(?[01]\d{2}[)-]?\d\s?\d{7,8}'
作者: xqqxjnt1988    时间: 2016-2-4 16:01
我看你们都是写的一行
:)
我却写的是三个,然后or的

m1 = re.findall(r'\(\d{3,4}\)\d{8}',text3)     #匹配括号的话用\(和\)  此句话匹配(0513)85602495
    m2 = re.findall(r'\d{3,4}\-\d{8}',text3)
    m3 = re.findall(r'\d{3,4}\s\d{8}',text3)
   
    if m1 or m2 or m3:
        print m1
        print "\n"
        print m2
        print "\n"
        print m3
这样写不好吗,大神们
作者: catherinemic    时间: 2016-2-22 20:09
^1\d*[x]?
以1开头的一串数字,数字结尾有字母x,也可以没有。有的话就带上x。

crossin老师,复习正则表达式的时候发现这个地方又有问题了,为什么x要用中括号括起来呢,直接^1\d*x?可以吗?
作者: crossin先生    时间: 2016-2-22 22:54
catherinemic 发表于 2016-2-22 20:09
^1\d*[x]?
以1开头的一串数字,数字结尾有字母x,也可以没有。有的话就带上x。

中括号是可选,不括起来就一定得有x
作者: crossin先生    时间: 2016-2-22 22:58
catherinemic 发表于 2016-2-22 20:09
^1\d*[x]?
以1开头的一串数字,数字结尾有字母x,也可以没有。有的话就带上x。

中括号表示可选,如果不加,那就必须要有x
===================
更正:这里中括号加不加效果是一样的
作者: catherinemic    时间: 2016-2-24 10:56
crossin先生 发表于 2016-2-22 22:58
中括号表示可选,如果不加,那就必须要有x

如果不加中括号,x后还有?表示连续0-1次,0次的话不就表示可以没有x吗?
作者: crossin先生    时间: 2016-2-24 11:26
catherinemic 发表于 2016-2-24 10:56
如果不加中括号,x后还有?表示连续0-1次,0次的话不就表示可以没有x吗?

sorry,我这里搞错了,的确不用加[]也是一样的
作者: kuaikemai    时间: 2016-6-4 15:41
老师,在本次课中你说正则表达式也可以用来检测输入的信息是否符合规则。
像输入用户名、身份证号码等。具体的操作是怎样的呢?
我自己的想法是,将input的字符串split后成list
然后运用re.findall应用规则,并得出另一个list m
用if语句看他们是不是 = =
这样是不是正确?
作者: crossin先生    时间: 2016-6-4 22:37
kuaikemai 发表于 2016-6-4 15:41
老师,在本次课中你说正则表达式也可以用来检测输入的信息是否符合规则。
像输入用户名、身份证号码等。具 ...

你这样是可行的的。不过正则本身就可以用^和$限定开头和结尾,限定了之后,能匹配出来就是正确,匹配不出来就是错误
作者: zgd1219    时间: 2016-8-26 16:32
问一下楼主,我把号码以正则表达式匹配出来,想写入一个txt,writelines写入要怎么加换行呢,不处理号码都是连着排成一行,如下格式:
(021)88776543010-55667890025844533620571 66345673
如果把列表转换成字符串加上\n写入,虽然能换行了,但是txt中的号码会带上括号和引号很别扭,如下:
['(021)88776543']
['010-55667890']
['02584453362']
['0571 66345673']
要如何在txt文件中得到下面的格式呢?
(021)88776543
010-55667890
02584453362
0571 66345673


作者: crossin先生    时间: 2016-8-26 23:45
zgd1219 发表于 2016-8-26 16:32
问一下楼主,我把号码以正则表达式匹配出来,想写入一个txt,writelines写入要怎么加换行呢,不处理号码都 ...

是应该列表中的每个元素后面加上\n,然后writelines。而不是直接转字符串,你转字符串的时候把list的[]也给转进去了
作者: zgd1219    时间: 2016-8-29 08:26
crossin先生 发表于 2016-8-26 23:45
是应该列表中的每个元素后面加上\n,然后writelines。而不是直接转字符串,你转字符串的时候把list的[]也 ...

谢谢,按楼主说的写就可以了
        m[0] = m[0] + '\n'
        n.writelines(m)
作者: Tony    时间: 2016-10-19 13:01
请问:对于号码匹配
我写了如下:r'[(\d{1}]\d{2}[)-\d{1}][\d{7}\d{8}(\s\d{8})]'
请问是哪里出来问题?
作者: crossin先生    时间: 2016-10-19 14:04
Tony 发表于 2016-10-19 13:01
请问:对于号码匹配
我写了如下:r'[(\d{1}]\d{2}[)-\d{1}][\d{7}\d{8}(\s\d{8})]'
请问是哪里出来问题? ...

中括号里表示任选一个字符
你不能把 \d{7} \d{8} 都放到中括号里,这样它还是认为是任意一个数字
\d{7,8}这样倒是可以
作者: Tony    时间: 2016-10-19 15:54
crossin先生 发表于 2016-10-19 14:04
中括号里表示任选一个字符
你不能把 \d{7} \d{8} 都放到中括号里,这样它还是认为是任意一个数字
\d{7,8} ...

我将表达式改成了这个:
r'[(\d{2,3}][)-]\d{7,8}'
结果,不可以识别:010-55667890这类的
它识别的结果是:['0-55667890']
请问,这是怎么回事?

作者: crossin先生    时间: 2016-10-20 13:50
Tony 发表于 2016-10-19 15:54
我将表达式改成了这个:
r'[(\d{2,3}][)-]\d{7,8}'
结果,不可以识别:010-55667890这类的

还是一样啊,你用
[(\d{2,3}][)-]
那它还是会只认其中任何一个字符
作者: Tony    时间: 2016-10-21 08:44
对,就是。在第一个[]中它要么识别出( 要么识别出d{2,3} 在第二个[]中,它要么识别出) 要么识别出- 最后识别出d{7,8}
而现在是它识别出了第二个中的- 以及后面的d{7,8} 在第一个中,只识别出了1个数字字符。而我写的是d{2,3}
我想问的是为什么会出现这种情况

作者: crossin先生    时间: 2016-10-21 13:27
Tony 发表于 2016-10-21 08:44
对,就是。在第一个[]中它要么识别出( 要么识别出d{2,3} 在第二个[]中,它要么识别出) 要么识别出- 最后识 ...

d{2,3}别放在中括号里,和前面说的问题一样,你放在中括号里,不会给你区分是(或d{2,3},而是整个这些里面任何一个字符
作者: Tony    时间: 2016-10-21 15:42
crossin先生 发表于 2016-10-21 13:27
d{2,3}别放在中括号里,和前面说的问题一样,你放在中括号里,不会给你区分是(或d{2,3},而是整个这些里面 ...

也就是说这样:[(\d{2,3}]
对于:
(
1
12
123
均可以识别出来?

作者: crossin先生    时间: 2016-10-22 15:29
Tony 发表于 2016-10-21 15:42
也就是说这样:[(\d{2,3}]
对于:
(

不是,你这么写,和你写[(\d],甚至[(\d+]是一个效果,中括号里是任取一个字符,所以不管你里面的规则几个,加了中括号都是任取一个
作者: Tony    时间: 2016-10-23 21:00
crossin先生 发表于 2016-10-22 15:29
不是,你这么写,和你写[(\d],甚至[(\d+]是一个效果,中括号里是任取一个字符,所以不管你里面的规则几 ...

哦,总算明白了。谢谢了哈!
还有,学完了这个,请问有好的书籍或者练手项目推荐没?
作者: crossin先生    时间: 2016-10-23 22:43
Tony 发表于 2016-10-23 21:00
哦,总算明白了。谢谢了哈!
还有,学完了这个,请问有好的书籍或者练手项目推荐没? ...

可以看看这个
《Python进阶》
https://eastlakeside.gitbooks.io/interpy-zh/content/
作者: Tony    时间: 2016-10-24 08:25
crossin先生 发表于 2016-10-23 22:43
可以看看这个
《Python进阶》
https://eastlakeside.gitbooks.io/interpy-zh/content/

谢谢crossin 先生
作者: swinh    时间: 2017-7-15 18:08
crossin先生 发表于 2013-11-3 21:11
我也觉得没问题

[)- ]python3.x报错
作者: crossin先生    时间: 2017-7-16 17:56
swinh 发表于 2017-7-15 18:08
[)- ]python3.x报错

确实,中括号里的 - 需要转义为 \-,除非在括号开头或者结尾
因为同时它还有别的含义,表示从一个字符到另一个字符
作者: chchch0720    时间: 2017-9-22 10:41
先生,我用\(?0\d{2,3}[\) \-]?\d{8}
匹配一大串数字,比如0258445336205,他会把后一串的0也加进去,这怎么办呢
作者: crossin先生    时间: 2017-9-22 22:47
chchch0720 发表于 2017-9-22 10:41
先生,我用\(?0\d{2,3}[\) \-]?\d{8}
匹配一大串数字,比如0258445336205,他会把后一串的0也加进去,这怎 ...

\(?0\d{2,3}?[\) \-]?\d{8}

但你这个所有号码连在一起,本来就会有歧义,很难处理
作者: chchch0720    时间: 2017-9-25 10:05
crossin先生 发表于 2017-9-22 22:47
\(?0\d{2,3}?[\) \-]?\d{8}

但你这个所有号码连在一起,本来就会有歧义,很难处理 ...

好啦谢谢先生,主要是想把放在一起的几个号码分别匹配出来,现在OK了
作者: IRVING_LEE    时间: 2018-2-21 19:49
先生,不知道你还在不在:)
我第一次的时候在正则表达式两端加上了 \b
然后发现(021)88776543这个号码就被匹配成了021)88776543
这应该怎么理解呢?
作者: crossin先生    时间: 2018-2-22 13:05
IRVING_LEE 发表于 2018-2-21 19:49
先生,不知道你还在不在:)
我第一次的时候在正则表达式两端加上了 \b
然后发现(021)88776543这个号码就 ...

\b 的话,会识别标点和数字交界
作者: IRVING_LEE    时间: 2018-2-23 10:47
crossin先生 发表于 2018-2-22 13:05
\b 的话,会识别标点和数字交界

谢谢先生

作者: 宝丁    时间: 2018-5-15 10:12
先生好:
(?: exp)  #exp是正则表达式
这样子不捕获也不参与组号分配,这样做的原因是为了对那些只用一次的分组,省内存嘛?
还是因为别的原因,
在什么情况下我们会用到呢?

作者: crossin先生    时间: 2018-5-16 09:40
宝丁 发表于 2018-5-15 10:12
先生好:
(?: exp)  #exp是正则表达式
这样子不捕获也不参与组号分配,这样做的原因是为了对那些只用一次的 ...

你不要捕获exp匹配的内容,只需要用它来定位
作者: loveting0jie    时间: 2018-6-22 11:25
[(]?0\d{2,3}[ )-]?\d{8}
\(?0\d{2,3}[) -]?\d{7,8}
\(0\d{2,3}\)\d{8}|0\d{2,3}[ -]?\d{8}
这样写感觉有局限性,当大量人员数据包含(手机号、身份证号、座机号等等),这个不光会匹配座机号,也会将手机号和身份证号的部分内容截取出来,我只需要座机号要怎么处理?
作者: loveting0jie    时间: 2018-6-22 11:28
loveting0jie 发表于 2018-6-22 11:25
[(]?0\d{2,3}[ )-]?\d{8}
\(?0\d{2,3}[) -]?\d{7,8}
\(0\d{2,3}\)\d{8}|0\d{2,3}[ -]?\d{8}

例如:
import re
f = open('J:\黄杰\学习文件\练习\正则表达式\员工信息登记表.txt')
data = f.read()

a = re.findall(r'\(0\d{2,3}\)\d{8}|0\d{2,3}[ -]?\d{8}',data)
print(a)

f.close()结果:
['098219781020', '010619920410', '092319841019', '011519850315', '010619860329', '082219900115', '050219900104', '005198703026', '011978021509', '092119840108', '081199508113', '062119970505', '(021)88776543', '050219940911', '010-55667890', '098419950322', '011519871217', '0571 66345673', '004199303160', '022219880115', '058319890712', '062519920228', '032419941023', '098119940110', '068219831108', '006198510077']



作者: crossin先生    时间: 2018-6-22 16:07
loveting0jie 发表于 2018-6-22 11:28
例如:
import re
f = open('J:\黄杰\学习文件\练习\正则表达式\员工信息登记表.txt')

这要看你数据里具体是怎样的形式,有分行或空格的话,可以通过 \b 边界符来避免手机号和身份证号
前面课程有提到这个符号




欢迎光临 Crossin的编程教室 (https://bbs.crossincode.com/) Powered by Discuz! X2.5