《python核心编程(第三版)》第1章 正则表达式 练习答案

成为程序员已经有好多年了,第一次开通博客,欢迎同好批评,点评。
最近迷恋上了python,看完了《python基础教程》后,又抱着一本《python核心编程(第三版)》开始啃了起来。
看完了第一章,发现了很多非常好的习题,自己尝试做了一些,但是却在网上找不到很全很好的答案。于是决定自己来写一写,记录一下自己的答案,每个答案都是自己上级实测的。

python环境为2.7.13,工具为JetBrains PyCharm 2017.1.1 x64

# 复制代码前,请import
import re

1-1 识别后续的字符串:bat, bit, but, hat, hit或者hut。

# 1-1
# 输出结果['bat', 'bit', 'but', 'hat', 'hit', 'hut']
pattern = r'[bh][aiu]t'
string = 'asdfbatkkjbitllwbutpphatoouhitwwhut'
print re.findall(pattern, string)

1-2 匹配由单个空格分隔单词对,也就是姓和名。

# 1-2
# 输出结果['Tom', 'Jerry', 'Hello', 'Bye', 'House', 'Good', 'God']
pattern = r'(.*)\s(.*)'
string = 'Tom Jerry Hello Bye House Good God'
print re.match(pattern, string).group()

1-3 匹配由单个逗号和单个空白分隔符分隔的任何单词和单个字母,如姓氏的首字符

# 1-3
# 输出结果S.N.Owfall 和 S.N. Owfall
pattern = r'([A-Z]\.)+ ?[A-Z][a-z]+'
string1 = 'S.N.Owfall'
string2 = 'S.N. Owfall'
print re.match(pattern, string1).group()
print re.match(pattern, string2).group()

1-4 匹配所有有效Python标识符的集合

# 1-4
pattern = r'[A-Za-z_]\w+'

1-5 根据读者当地的格式,匹配街道地址(使你的正则表达式足够通用,来匹配任意数量的街道单词,包括类型名称)。例如,美国结果地址使用如下格式:1180 Bordeaux Drive。使你的正则表达式足够灵活,以支持多单词的结果名称,如3120 De la Cruz Boutlevard。

# 1-5
pattern = r'\d+ [A-Za-z ]+'
string1 = '1180 Bordeaux Drive'
stirng2 = '3120 De la Cruz Boulevard'
print re.match(pattern, string1).group()
print re.match(pattern, string2).group()

1-6 匹配以“www”起始且以“.com”结尾的简单Web域名;例如,www.://www.yahoo.com/。选做题:你的正则表达式也可以支持其他高级域名,如.edu、.net等(例如,http://www.foothilledu)

# 1-6
pattern = r'((http:|https:)//)?[w]{3}\.\w+(.edu|.com|.net)'
string = 'http://www.foothill.edu'
print re.match(pattern, string).group()

1-7 匹配所有能够表示Python整数的字符串集

# 1-7
pattern = r'-?(\d+)'
string = '-212312'
print re.match(pattern, string).group()

1-8 匹配所有能够表示Python长整数的字符串集

# 1-8
pattern = r'-?(\d+)L'
string = '-212312L'
print re.match(pattern, string).group()

1-9 匹配所有能够表示Python浮点数的字符串集

# 1-9
pattern = r'-?\d+\.\d+'
string = '-3.1415926'
print re.match(pattern, string).group()

1-10 匹配所有能够表示Python复数的字符串集

# 1-10
pattern = r'-?\d+\.?\d+\+\d+\.+\d+j'
string = '-1.4+1.5j'
print re.match(pattern, string).group()

1-11 匹配所有能够表示有效电子邮件地址的集合(从一个宽松的正则表达式开始,然后尝试使它尽可能严谨,不过要保持正确的功能)

# 1-11
pattern = r'\w+@\w+\.com'
string = 'abc_abc111@abc111_abc.com'
print re.match(pattern, string).group()

1-12 匹配所有能够表示有效的网址的集合(URL)(从一个宽松的正则表达式开始,然后尝试使它尽可能严谨,不过要保持正确的功能)

# 1-12
pattern = r'((http:|https:)//)?([w]{3}\.)?\w+\.\w+'
string = 'http://foothill.edu'
print re.match(pattern, string).group()

1-13 type()。内置函数type()返回一个类型对象,如下所示,该对象将表示为一个Pythonic类型的字符串。

>>> type(0)  
<type 'int'> 
>>> type(.34)  
<type 'float'> 
>>> type(dir)  
<type 'builtin_function_or_method'>

创建一个能够从字符串中提取实际类型名称的正则表达式。函数将对类似于的字符串返回int(其他类型也是如此,如 ‘float’ 、’builtin_function_or_method’ 等)。注意:你所实现的值将存入类和一些内置类型的_name_属性中。

1-14 处理日期。1.2节提供了来匹配单个或者两个数字字符串的正则表达式模式,来表示1~9的月份(0?[1-9])。创建一个正则表达式来表示标准日历中剩余三个月的数字。

pattern = r'1[0-2]'
string = '12'
print re.search(pattern, string).group()

1-15 处理信用卡号码。1.2节还提供了一个能够匹配信用卡(CC)号码([0-9]{15,16})的正则表达式模式。然而,该模式不允许使用连字符来分割数字块。创建一个允许使用连字符的正则表达式,但是仅能用于正确的位置。例如,15位的信用卡号码使用4-6-5的模式,表明4个数字-连字符-6个数字-连字符-5个数字;16位的信用卡号码使用4-4-4-4的模式。记住,要对整个字符串进行合适的分组。选做题:有一个判断信用卡号码是否有效的标准算法。编写一些代码,这些代码不但能够识别具有正确格式的号码,而且能够识别有效的信用卡号码。

# 1-15
pattern = r'([0-9]{4}-[0-9]{6}-[0-9]{5})|([0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{4})'
string = '4444-444465-44446'
print re.search(pattern, string).group()

使用gendata.py。下面一组练习(1-16~1-27)专门处理由gendata.py生成的数据。在尝试练习1-17和1-18之前,读者需要先完成练习1-16以及所有正则表达式。

1-16 为gendata.py更新代码,使数据直接输出到redata.txt而不是屏幕。

from random import randrange, choice
from string import ascii_lowercase as lc
from sys import maxint
from time import ctime


tlds = ('com', 'edu', 'net', 'org', 'gov')
f=open('redata.txt','w')
for i in xrange(randrange(1, 30)):
    dtint = randrange(maxint)
    dtstr = ctime(dtint)
    llen = randrange(4, 8)
    login = ''.join(choice(lc) for j in xrange(llen))
    dlen = randrange(llen, 13)
    dom = ''.join(choice(lc) for j in xrange(dlen))
    input = str(dtstr) + '::' + str(login) + '@' + str(dom) + '.' + str(choice(tlds)) + '::' + str(dtint) + '-' + str(llen) + '-' + str(dlen)
    f.write(input + '\n')

1-17 判断在redata.tex中一周的每一天出现的次数(换句话说,读者也可以计算所选择的年份中每个月中出现的次数)。

week_list = []
month_list = []
f = open('redata.txt', 'r')
for eachLine in f:
    week_list.append(re.split(r'\s+', eachLine)[0])
    month_list.append(re.split(r'\s+', eachLine)[1])

week_day_tmp_list = set(week_list)
month_tmp_list = set(month_list)
print "____________________"
print "Week Times:"
for item in week_day_tmp_list:
    print "%s appears %d time(s)" %(item, week_list.count(item))

print "____________________"
print "Month Times:"
for item in month_tmp_list:
    print "%s appears %d time(s)" %(item, month_list.count(item))
f.close()

1-18 通过确认整数字段中的第一个整数匹配在每个输出行起始部分的时间戳,确保在redata.txt中没有数据损坏。

# 1-18
from time import ctime


num_pattern = r'.+::(\d+)-'
time_stamp_pattern = r'^(.{24})::.+'
try:
    f = open('redata.txt', 'r')
    for i, eachLine in enumerate(f):
        # 得到第一个整数
        second = re.search(num_pattern, eachLine.strip()).group(1)
        time_stamp_str = re.search(time_stamp_pattern, eachLine.strip()).group(1)
        # 匹配时间戳是否正确
        if time_stamp_str != str(ctime(int(second))):
            print "Line %d is not WRONG! Correct Timestamp is %s" %(i, time_stamp_str)
        else:
            print "This Line is OK!"
except ValueError as value_err:
    print "First Num Is Not The Type Of INT:" + value_err.message
except IOError as io_err:
    print('File Error:' + io_err.message)
finally:
    f.close()

创建以下正则表达式。

1-19 提取每行中完整的时间戳。

# 1-19
time_stamp_pattern = r'^(.{24})::.+'
try:
    f = open('redata.txt', 'r')
    for eachLine in f:
        print re.search(time_stamp_pattern, eachLine.strip()).group(1)
except IOError as io_err:
    print('File Error:' + io_err.message)
finally:
    f.close()

1-20 提取每行中完整的电子邮件地址。

# 1-20
email_pattern = r'.+::(.+)::.+'
try:
    f = open('redata.txt', 'r')
    for eachLine in f:
        print re.search(email_pattern, eachLine.strip()).group(1)
except IOError as io_err:
    print('File Error:' + io_err.message)
finally:
    f.close()

1-21 仅仅提取时间戳中的月份。

# 1-21
month_pattern = r'^\w{3}\s(\w{3}).+'
try:
    f = open('redata.txt', 'r')
    for eachLine in f:
        print re.search(month_pattern, eachLine.strip()).group(1)
except IOError as io_err:
    print('File Error:' + io_err.message)
finally:
    f.close()

1-22 仅仅提取时间戳中的年份。

# 1-22
year_pattern = r'.+(\d{4})::.+'
try:
    f = open('redata.txt', 'r')
    for eachLine in f:
        print re.search(year_pattern, eachLine.strip()).group(1)
except IOError as io_err:
    print('File Error:' + io_err.message)
finally:
    f.close()

1-23 仅仅提取时间戳中的时间(HH:MM:SS)。

# 1-23
time_pattern = r'.+(\d{2}:\d{2}:\d{2}).+'
try:
    f = open('redata.txt', 'r')
    for eachLine in f:
        print re.search(time_pattern, eachLine.strip()).group(1)
except IOError as io_err:
    print('File Error:' + io_err.message)
finally:
    f.close()

1-24 仅仅从电子邮件地址中提取登录名和域名(包括主域名和高级域名一起提取)。

# 1-24
pattern = r'.+::(\w+)@(\w+\.\w+).+'
try:
    f = open('redata.txt', 'r')
    for eachLine in f:
        # 登录名
        print re.search(pattern, eachLine.strip()).group(1)
        # 主域名和高级域名
        print re.search(pattern, eachLine.strip()).group(2)
except IOError as io_err:
    print('File Error:' + io_err.message)
finally:
    f.close()

1-25 仅仅从电子邮件地址中提取登录名和域名(包括主域名和高级域名)。

# 1-25
pattern = r'.+::(\w+)@(\w+)\.(\w+).+'
try:
    f = open('redata.txt', 'r')
    for eachLine in f:
        # 登录名
        print re.search(pattern, eachLine.strip()).group(1)
        # 主域名
        print re.search(pattern, eachLine.strip()).group(2)
        # 高级域名
        print re.search(pattern, eachLine.strip()).group(3)
except IOError as io_err:
    print('File Error:' + io_err.message)
finally:
    f.close()

1-26 使用你的电子邮件地址替换每一行数据中的电子邮件地址。

# 1-26
pattern = r'(.+::)(\w+@\w+\.\w+)(::.+)'
my_email = r'\[email protected]\3'
try:
    f = open('redata.txt', 'r')
    for eachLine in f:
        print re.sub(pattern, my_email, eachLine.strip())
except IOError as io_err:
    print('File Error:' + io_err.message)
finally:
    f.close()

1-27 从时间戳中提取月、日和年,然后以“日,月,年”的格式,每一行仅仅迭代一次。(书上题目有问题,我自己修改的题目)

# 1-27
pattern = r'(.+)(\w{3})\s(\d{2})(.+)(\d{4})(.+)'
my_email = r'\1\3 \2\4\5\6'
try:
    f = open('redata.txt', 'r')
    for eachLine in f:
        print re.sub(pattern, my_email, eachLine.strip())
except IOError as io_err:
    print('File Error:' + io_err.message)
finally:
    f.close()

处理电话号码。对于练习1-28和1-29,回顾1.2节介绍的正则表达式\d{3}-\d{3}-\d{4},它匹配电话号码,但是允许可选的区号作为前缀。更新正则表达式,使它满足以下条件。

1-28 区号(三个整数集合中的第一部分和后面的连字符)是可选的,也就是说,正则表达式应当匹配800-555-1212,也能匹配555-1212。

# 1-28
pattern = r'(\d{3}-)?\d{3}-\d{4}'
phone = '555-1212'
print re.match(pattern, phone).group()

1-29 支持使用圆括号或者连字符连接的区号(更不用说是可选的内容);使正则表达式匹配800-555-1212、555-1212以及(800)555-1212。

# 1-29
pattern = r'(\d{3}-|\(\d{3}\)-|\d{3}-)?\d{3}-\d{4}'
phone1 = '(888)-555-1212'
phone2 = '555-1212'
phone3 = '888-555-1212'
print re.match(pattern, phone1).group()
print re.match(pattern, phone2).group()
print re.match(pattern, phone3).group()

你可能感兴趣的:(习题答案,python,python核心编程)