Python核心编程(第二版)正则表达式练习题解

时间:2022-07-25 19:52:09

15-1. 识别下列字符串:“bat,” “bit,” “but,” “hat,” “hit,” 或 “hut”

from re import match

word = raw_input('input: ')
m = match('^[bh][aiu]t$', word)
if m is not None:
print m.group()
else:
print 'not match'

15–2. 匹配用一个空格分隔的任意一对单词,比如,名和姓。

from re import match

name = raw_input('input: ')
m = match('.+\s.+', name) #或者'\b\w+ \w+\b'
if m is not None:
print m.group()
else:
print 'not match'

15–3. 匹配用一个逗号和一个空格分开的一个单词和一个字母。例如,英文人名中的姓和名
的首字母。

from re import match

name = raw_input('input: ')
m = match('.+, \w', name) # 或者 '.+,\s.+'
if m is not None:
print m.group()
else:
print 'not match'

15-4 匹配所有合法的 Python 标识符。

from re import match

n = raw_input('input: ')
m = match('^[_a-zA-Z][_\w]*', n) # r'\b[a-zA-Z_](\w|_)*\b'
if m is not None:
print m.group()
else:
print 'not match'

15–5. 请根据您(读者)本地关于地址的格式写法匹配一个街道地址(你写出的正则表达式要
尽可能通用以匹配任意数目的表示街道名字的单词,包括类型指示)。比如,美国的街道地址使用这
样的格式:1180 Bordeaux Drive. 使你写的正则表达式尽可能通用,要求能够匹配多个单词的街道
名字,如:3120 De la Cruz Boulevard.

from re import match

n = raw_input('input: ')
m = match('\d+(\s[A-Za-z]+)+', n)
if m is not None:
print m.group()
else:
print 'not match'

15–6. 匹配简单的以“www.”开头,以“.com”作结尾的 Web 域名,例如:www.yahoo.com. 附
加题:使你写的正则表达式还支持其他*域名:.edu, .net 等,比如:www.ucsc.edu.

from re import match

n = raw_input('input: ')
m = match('^w{3}\.\w+\.(com|edu|net)$', n)
if m is not None:
print m.group()
else:
print 'not match'

15-7. 匹配全体 Python 整数的字符串表示形式的集合。

from re import match

n = raw_input('input: ')
m = match('(([\da-fA-F]+)|(( *\d+ *)+))', n)
if m is not None:
print m.group()
else:
print 'not match'

15–8. 匹配全体 Python 长整数的字符串表示形式的集合。

from re import match

n = raw_input('input: ')
m = match('( *\d+[lL] *)+', n)
if m is not None:
print m.group()
else:
print 'not match'

15–9. 匹配全体 Python 浮点数的字符串表示形式的集合。

from re import match

n = raw_input('input: ')
m = match('\d+(\.\d)*', n)
if m is not None:
print m.group()
else:
print 'not match'

15–10. 匹配全体 Python 复数的字符串表示形式的集合。

from re import match

n = raw_input('input: ')
m = match('( *\d*\+*(\dj)+ *)+', n)
if m is not None:
print m.group()
else:
print 'not match'

15–11. 匹配所有合法的电子邮件地址(先写出一个限制比较宽松的正则表达式,然后尽可能加
强限制条件,但要保证功能的正确性)。

from re import match

n = raw_input('input: ')
m = match('\w+@\w+(.com)$', n)
if m is not None:
print m.group()
else:
print 'not match'

15–12.匹配所有合法的 Web 网站地址(URLs)(先写出一个限制比较宽松的正则表达式,然后尽
可能加强限制条件,但要保证功能的正确性)。

from re import match

n = raw_input('input: ')
m = match('^www.\w+(.com|.net|.edu)$', n)
if m is not None:
print m.group()
else:
print 'not match'

15–13. type(). type()内建函数返回一个对象类型,此对象显示为 Python 的字符串形式,
如下所示:
>>> type(0)
<type 'int'>
>>> type(.34)
<type 'float'>
>>> type(dir)
<type 'builtin_function_or_method'>
请写一个正则表达式,能从这个字符串中提取出类型的名字。 你的函数能实现以下功能:如
果以字符串“<type 'int'>”做输入,会返回类型“int”.(返回其他类型也同理,如,返回类型‘float’,
‘builtin_function_or_method’等) 提示:正确的结果保存在类和某些内建类型的__name__属性
里。

from re import match

n = type(.34)
m = match("<type '(\w+)'>", str(n))
if m is not None:
print m.group(1)

15–14.正则表达式。在 15.2 小节里,我们给出一个匹配由一位或两位数字代表一月到九月的
字符串形式(“0?[1-9]”)。 请写出一个正则表达式表示标准日历上其它的三个月(十月、十一月、
十二月)。

from re import match

n = raw_input('input: ')
m = match('^(0?[1-9])$|^(10|11|12)$', n)
if m is not None:
print m.group()
else:
print 'not match'

5–15. 正则表达式。在 15.2 小节里,我们给出一个匹配信用卡卡号的模式:( “[0-9]{15,16}”).
但这个模式不允许用连字符号分割信用卡卡号中的数字。请写出一个允许使用连字符的正则表达式,
但要求连字符必须出现在正确的位置。例如,15 位的信用卡卡号的格式是 4-6-5,表示四个数字,一
个连字符,后面接六个数字、一个连字符,最后是五个数字。16 位的信用卡卡号的格式是 4-4-4-4,
数位不足时,添 0 补位。附加题:有一个用于确定某个信用卡卡号是否合法的算法。请写一段代码,
它不但能识别格式正确的信用卡卡号,还能验证它的有效性。

from re import match

n = raw_input('input: ')
m = match('^(\d{4}-\d{4}-\d{4}-\d{4})|(\d{4}-\d{6}-\d{5})$', n)
if m is not None:
print m.group()
else:
print 'not match'

下面几个问题(练习 15–16 到 15–27)专门处理 gendata.py 生成的数据。在做练习 15–17 和
15–18 之前,请先把练习 15–16 和所有正则表达式做出来。

15-16.修改gendata.py的代码,使数据直接写入文件redata.txt中,而不是输出到屏幕上。

from random import randint, choice
from string import lowercase
from sys import maxint
from time import ctime
from os import linesep doms = ('com', 'edu', 'net', 'org', 'gov')
f = open('redata.txt', 'w') for i in range(randint(5, 10)):
dtint = randint(0, maxint-1)
dtstr = ctime(dtint) shorter = randint(4, 7)
em = ''
for j in range(shorter):
em += choice(lowercase) longer = randint(shorter, 12)
dn = ''
for j in range(longer):
dn += choice(lowercase) st = '%s::%s@%s.%s::%d-%d-%d' % (dtstr, em,
dn, choice(doms), dtint, shorter, longer)
st += linesep
f.write(st) f.close()
print 'done'

15–17. 统计生成的 redata.txt 文件中,星期中的每一天出现的次数(或统计各月份出现的次
数)。

import re

week = {}.fromkeys(('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'), 0)
st = '^(' + '|'.join(week.keys()) + ')' with open('redata.txt') as f:
for line in f:
m = re.match(st, line)
if m is not None:
week[m.group()] += 1
print week

15–18.通过检查每个输出行中整数字段部分的第一个整数是否和该行开头的时间戳相匹配来
验证 redata.txt 中的数据是否完好。

import re
from time import mktime, strptime with open('redata.txt') as f:
for line in f:
m = re.match('(.+)::.+::(\d+)', line)
if m is not None:
time1 = mktime(strptime(m.group(1)))
time2 = float(m.group(2))
if time1 == time2:
print line, '...well'

15–19. 提取出每行中完整的时间戳字段。

import re

with open('redata.txt') as f:
for line in f:
m = re.match('.+::(.+)::(\d+)', line)
if m is not None:
print m.group(1)

15–20. 提取出每行中完整的电子邮件地址。

import re

with open('redata.txt') as f:
for line in f:
m = re.match('.+::(.+)::(\d+)', line)
if m is not None:
print m.group(1)

15–21. 只提取出时间戳字段中的月份。

import re

with open('redata.txt') as f:
for line in f:
m = re.match('.+? (.+?) .+', line)
if m is not None:
print m.group(1)

15-22. 只提取出时间戳字段中的年份。

import re

with open('redata.txt') as f:
for line in f:
m = re.match('.+ (.+?)::', line)
if m is not None:
print m.group(1)

15–23. 只提取出时间戳字段中的值(格式:HH:MM:SS)。

import re

with open('redata.txt') as f:
for line in f:
m = re.match('.+(\d\d:\d\d:\d\d)', line)
if m is not None:
print m.group(1)

15-24. 只从电子邮件地址中提取出登录名和域名(包括主域名和*域名,二者连在一起)。

import re

with open('redata.txt') as f:
for line in f:
m = re.search('::(.+)@(.+)::', line)
if m is not None:
print m.group(1), m.group(2)

15-25. 只从电子邮件地址中提取出登录名和域名(包括主域名和*域名,二者分别提取)。

def splitLoginNameAndDomainName(fileObj):
  EMailAddrPattern = re.compile(r'::([a-z]+)@([a-z]+\.[a-z]+)::')
while True:
  line = fileObj.readline()
if line:
  match = EMailAddrPattern.search(line)
  print ' '.join([match.group(1), match.group(2)])
else:
  break

15–26. 将每行中的电子邮件地址替换为你自己的电子邮件地址。

import re

email ='test@yahoo.jp'
ls = [] with open('redata.txt') as f:
for line in f:
ls.append(re.sub('\w+@.+::', email + '::', line)) with open('redata', 'w') as f:
f.write(''.join(ls))

15–27. 提取出时间戳中的月、日、年,并按照格式“月 日,年”显示出来,且每行仅遍
历一次。

import re

with open('redata.txt') as f:
for line in f:
m = re.match('\w+ (\w+ \w+) .+ (\d+)', line)
if m is not None:
print '%s, %s' % (m.group(1), m.group(2))

我们在小节 15.2 中使用的一个匹配电话号码的正则表达式,其中电话号码允许包含可选的区号
前缀 : \d{3}-\d{3}-\d{4}. 请在练习 15–28 和 15–29 中, 修改这个正则表达式,使它满足:
15–28.区号(第一组的三个数字和它后面的连字符)是可选的,即,你写的正则表达式对
800-555-1212 和 555-1212 都可以匹配。

import re

n = raw_input('input: ')
m = re.match('(\d{3}-)*\d{3}-\d{4}', n)
if m is not None:
print m.group()
else:
print 'Not match'

15–29.区号中可以包含圆括号或是连字符,而且它们是可选的,就是说你写的正则表达式可以
匹配 800-555-1212, 或 555-1212, 或(800) 555-1212.

from re import match

n = raw_input('input: ')
m = match('(\d{3}-|\(\d{3}\))*\d{3}-\d{4}', n)
if m is not None:
print m.group()
else:
print 'not match'

做题的过程中学会了很多,本来以为正则特别难,通过这次练习,现在已经不怕它了。