按照练习 1-1~1-12 的要求创建正则表达式。
1-1 识别后续的字符串:“bat”、“bit”、“but”、“hat”、“hit”或者“hut”。
>>> import re
>>> data = "bat, bit, but, hat, hit, hut"
>>> patt1_1 = '[bh][aiu]t'
>>> re.findall(patt1_1, data)
['bat', 'bit', 'but', 'hat', 'hit', 'hut']
1-2 匹配由单个空格分隔的任意单词对,也就是姓和名。
>>> patt1_2 = '\w+ \w+'
>>> data1_2 = """
... zhang san
... li si
... wang wu
... """
>>> re.findall(patt1_2, data1_2)
['zhang san', 'li si', 'wang wu']
1-3 匹配由单个逗号和单个空白符分隔的任何单词和单个字母,如姓氏的首字母。
>>> patt1_3 = '[, ]?(\w+)[, ]?'
>>> data1_3 = """
... zhang san
... li si
... wang wu
... """
>>> re.findall(patt1_3, data1_3)
['zhang', 'san', 'li', 'si', 'wang', 'wu']
1-4 匹配所有有效 Python 标识符的集合。
>>> patt1_4 =r'\b[A-Za-z_][A-Za-z0-9_]*\b' # \b 是声明单词开头
>>> data1_4 = '9wat Ea43 _oa5 *uyz1 foo P 0 p'
>>> re.findall(patt1_4, data1_4)
['Ea43', '_oa5', 'uyz1', 'foo', 'P', 'p']
1-5 根据读者当地的格式,匹配街道地址(使你的正则表达式足够通用,来匹配任意数
量的街道单词,包括类型名称)。例如,美国街道地址使用如下格式:1180 Bordeaux
Drive。使你的正则表达式足够灵活,以支持多单词的街道名称,如 3120 De la Cruz
Boulevard。
patt1_5 = r"\d+\s([A-Za-z]+\s)"
1-6 匹配以“www”起始且以“.com”结尾的简单 Web 域名;例如,www://www. yahoo.com/。
patt1_6 = r"^www[\S]*\.com$"
选做题:你的正则表达式也可以支持其他高级域名,如.edu、.net 等(例如,
http://www.foothill.edu)。
patt1_6 = r"www[\S]*\.(com|edu|net)$"
1-7 匹配所有能够表示 Python 整数的字符串集。
patt1_7 = r"[-+]?\d+"
1-8 匹配所有能够表示 Python 长整数的字符串集。
patt1_8 = r"[-+]?\d+[lL]"
1-9 匹配所有能够表示 Python 浮点数的字符串集。
patt1_9 = r"[-+]?\d+\.\d*([e]\d+)?"
1-10 匹配所有能够表示 Python 复数的字符串集。
patt1_10 = r"([-+]?\d+\.\d*([e]\d+)?)?[+-]?[-+]?\d+\.\d*([e]\d+)?[Jj]"
1-11 匹配所有能够表示有效电子邮件地址的集合(从一个宽松的正则表达式开始,然
后尝试使它尽可能严谨,不过要保持正确的功能)。
"""
email 地址有两部分,[email protected] 不区分大小写
user 包括小写字母数字下划线点,英文字母开头
域名,各国文字的特定字符集、英文字母、数字及“-”(即连字符或减号)任意组合而成,
但开头及结尾均不能含有“-”,“-”不能连续出现 。
域名最长可达60个字节(包括后缀.com、.net、.org等)
"""
patt1_11 = r"[a-z][a-z0-9]*[_.]?[a-z0-9]+@([a-z0-9]+[-]?[a-z0-9]+\.)+\w"
1-12 匹配所有能够表示有效的网站地址的集合(URL)(从一个宽松的正则表达式开始,
然后尝试使它尽可能严谨,不过要保持正确的功能)。
patt1_12 = r"[a-z]+\://([a-z0-9]+[-]?[a-z0-9]+\.)+[\:\d+]?[/\w+]*"
1-13 type()。内置函数 type()返回一个类型对象,如下所示,该对象将表示为一个 Pythonic
类型的字符串。
>>> type(0)
<type 'int'>
>>> type(.34)
<type 'float'>
>>> type(dir)
<type 'builtin_function_or_method'>
创建一个能够从字符串中提取实际类型名称的正则表达式。函数将对类似于
注意:你所实现的值将存入类和一些内置类型的__name__属性中。
patt = r"^$"
1-14 处理日期。1.2 节提供了来匹配单个或者两个数字字符串的正则表达式模式,来表示 1~9 的月份(0?[1-9])。创建一个正则表达式来表示标准日历中剩余三个月的数字。
# 格式 MM/DD/[YY|YYYY]
patt = r"[01]?[0-9]/[0-3]?[0-9]/(\d{2}|\d{4})
1-15 处理信用卡号码。1.2 节还提供了一个能够匹配信用卡(CC)号码([0-9]{15,16})
的正则表达式模式。然而,该模式不允许使用连字符来分割数字块。创建一个允
许使用连字符的正则表达式,但是仅能用于正确的位置。例如,15 位的信用卡号
码使用 4-6-5 的模式,表明 4 个数字-连字符-6 个数字-连字符-5 个数字;16 位的
信用卡号码使用 4-4-4-4 的模式。记住,要对整个字符串进行合适的分组。选做题:
有一个判断信用卡号码是否有效的标准算法。编写一些代码,这些代码不但能够
识别具有正确格式的号码,而且能够识别有效的信用卡号码。
patt = (\d{4}-\d{6}-\d{5})|(\d{4}-\d{4}-\d{4}-\d{4})
使用 gendata.py。下面一组练习(1-16~1-27)专门处理由 gendata.py 生成的数据。
在尝试练习 1-17 和 1-18 之前,读者需要先完成练习 1-16 以及所有正则表达式。
1-16 为 gendata.py 更新代码,使数据直接输出到 redata.txt 而不是屏幕。
# 代码 gendata.py
#!/usr/bin/env python
"""
生成随机 登陆 日志
包括 日期 - 邮箱地址
"""
from random import randrange, choice
from string import ascii_lowercase as lc
from time import ctime
tlds = ('com', 'edu', 'net', 'org', 'gov')
txt = ""
for i in range(randrange(5, 11)):
dtint = randrange(2**32) # 选取随机数
dtstr = ctime(dtint) # 选取日期
llen = randrange(4, 8) # login 长度
login = ''.join(choice(lc) for j in range(llen))
dlen = randrange(llen, 13) # 域名 长度
dom = ''.join(choice(lc) for j in range(dlen))
txt += f'{dtstr}::{login}@{dom}.{choice(tlds)}::{dtint}-{llen}-{dlen}'+"\n"
with open('redata.txt', 'w') as f:
f.write(txt)
1-17 判断在 redata.txt 中一周的每一天出现的次数(换句话说,读者也可以计算所选择
的年份中每个月中出现的次数)。
txt = """
Sun Sep 22 09:07:03 1985::[email protected]::496199223-7-8
Thu Jul 5 08:10:06 2018::[email protected]::1530749406-4-4
Mon May 8 08:21:54 2017::[email protected]::1494202914-5-11
Thu Nov 30 01:13:41 2017::[email protected]::1511975621-4-12
Tue Mar 15 00:47:50 2072::[email protected]::3225199670-4-7
Fri Nov 3 03:56:11 2079::[email protected]::3466180571-5-10
Thu Jun 28 01:59:17 2035::[email protected]::2066579957-6-11
Thu Jan 14 10:49:02 2010::[email protected]::1263437342-7-12
Sat Jul 6 09:00:58 2030::[email protected]::1909530058-7-8
"""
import re
patt = r"(\w{3})\s+\w{3}\s+\d{1,2}"
day = re.findall(patt, txt)
day_dict={}
for i in day:
day_dict[i] = day_dict.get(i, 0) + 1
print(day_dict)
1-18 通过确认整数字段中的第一个整数匹配在每个输出行起始部分的时间戳,确保在
redata.txt 中没有数据损坏
txt = """
Sun Sep 22 09:07:03 1985::[email protected]::496199223-7-8
Thu Jul 5 08:10:06 2018::[email protected]::1530749406-4-4
Mon May 8 08:21:54 2017::[email protected]::1494202914-5-11
Thu Nov 30 01:13:41 2017::[email protected]::1511975621-4-12
Tue Mar 15 00:47:50 2072::[email protected]::3225199670-4-7
Fri Nov 3 03:56:11 2079::[email protected]::3466180571-5-10
Thu Jun 28 01:59:17 2035::[email protected]::2066579957-6-11
Thu Jan 14 10:49:02 2010::[email protected]::1263437342-7-12
Sat Jul 6 09:00:58 2030::[email protected]::1909530058-7-8
"""
import re
from time import ctime
patt = r"(\w{3}\s+\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+\d{4})::.*::(\d+)-\d-\d+"
day_list = re.findall(patt, txt)
for i in day_list:
if i[0] == ctime(int(i[1])):
print(True)
创建以下正则表达式。
1-19 提取每行中完整的时间戳。
patt = r"(\w{3}\s+\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+\d{4})"
1-20 提取每行中完整的电子邮件地址。
patt = r"\w+@\w+\.\w+"
1-21 仅仅提取时间戳中的月份。
patt = r"\w{3}\s+(\w{3})\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+\d{4}"
1-22 仅仅提取时间戳中的年份。
patt = r"\w{3}\s+\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2}\s+(\d{4})"
1-23 仅仅提取时间戳中的时间(HH:MM:SS)。
patt = r"\w{3}\s+\w{3}\s+\d{1,2}\s+(\d{2}:\d{2}:\d{2})\s+(\d{4})"
1-24 仅仅从电子邮件地址中提取登录名和域名(包括主域名和高级域名一起提取)。
patt = r"(\w+)@(\w+\.\w+)"
1-25 仅仅从电子邮件地址中提取登录名和域名(包括主域名和高级域名)。
patt = r"(\w+)@(\w+\.\w+)"
1-26 使用你的电子邮件地址替换每一行数据中的电子邮件地址。
newtxt = re.sub(patt, "[email protected]", txt)
1-27 从时间戳中提取月、日和年,然后以“月,日,年”的格式,每一行仅仅迭代一次。
处理电话号码。对于练习 1-28 和 1-29,回顾 1.2 节介绍的正则表达式\d{3}-\d{3}-\d{4}
它匹配电话号码,但是允许可选的区号作为前缀。更新正则表达式,使它满足以下条件。
patt = r"\w{3}\s+(\w{3})\s+(\d{1,2})\s+\d{2}:\d{2}:\d{2}\s+(\d{4})"
1-28 区号(三个整数集合中的第一部分和后面的连字符)是可选的,也就是说,正则
表达式应当匹配 800-555-1212,也能匹配 555-1212。
patt = r"(\d{3})?-\d{3}-\d{4}"
1-29 支持使用圆括号或者连字符连接的区号(更不用说是可选的内容);使正则表达式
匹配 800-555-1212、555-1212 以及(800)555-1212。正则表达式应用程序。下面练习在处理在线数据时生成了有用的应用程序脚本。
patt = r"((\d{3})?-\d{3}-\d{4})|(\(\d{3}\)\s+\d{3}-\d{4}"
下面三道题不会做,没太看懂题目。
1-30 生成 HTML。提供一个链接列表(以及可选的简短描述),无论用户通过命令
行方式提供、通过来自于其他脚本的输入,还是来自于数据库,都生成一个
Web 页面(.html),该页面包含作为超文本锚点的所有链接,它可以在 Web 浏
览器中查看,允许用户单击这些链接,然后访问相应的站点。如果提供了简短
的描述,就使用该描述作为超文本而不是 URL。
1-31 tweet 精简。有时候你想要查看由 Twitter 用户发送到 Twitter 服务的 tweet 纯文本。
创建一个函数以获取 tweet 和一个可选的“元”标记,该标记默认为 False,然
后返回一个已精简过的 tweet 字符串,即移除所有无关信息,例如,表示转推的
RT 符号、前导的“.”符号,以及所有#号标签。如果元标记为 True,就返回一
个包含元数据的字典。这可以包含一个键“RT”,其相应的值是转推该消息的用
户的字符串元组和/或一个键“#号标签”(包含一个#号标签元组)。如果值不存
在(空元组),就不要为此创建一个键值条目。
1-32 亚马逊爬虫脚本。创建一个脚本,帮助你追踪你最喜欢的书,以及这些书在亚马
逊上的表现(或者能够追踪图书排名的任何其他的在线书店)。例如,亚马逊对于
任何一本图书提供以下链接:http://amazon.com/dp/ISBN(例如,http://amazon.com/
dp/0132678209)。读 者可以改变域名,检查亚马逊在其他国家的站点上相同的图
书排名,例如德国(.de)、法国(.fr)、日本(.jp)、中国(.cn)和英国(.co.uk)。
使用正则表达式或者标记解析器,例如 BeautifulSoup、lxml 或者 html5lib 来解析
排名,然后让用户传入命令行参数,指明输出是否应当在一个纯文本中,也许包
含在一个电子邮件正文中,还是用于 Web 的格式化 HTML 中。