1.Github地址
2.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) |
---|---|---|
** Planning ** | 计划 | 20 |
· Estimate | · 估计这个任务需要多少时间 | 20 |
Development | 开发 | 1060 |
· Analysis | · 需求分析 (包括学习新技术) | 300 |
· Design Spec | · 生成设计文档 | 20 |
· Design Review | · 设计复审 | 20 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 10 |
· Design | · 具体设计 | 50 |
· Coding | · 具体编码 | 540 |
· Code Review | · 代码复审 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 60 |
Reporting | 报告 | 80 |
· Test Repor | · 测试报告 | 30 |
· Size Measurement | · 计算工作量 | 10 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 40 |
total | 合计 | 1160 |
3.设计与实现
设计过程
- 等级在输入的字符串中位置固定,在第一位,可以直接用序列号得到。
- 姓名位于“!”以及“,”之间,可以通过条件判断或者使用正则表达式匹配获得。
- 手机号长度固定为11位连续的数字,且手机号不会与其他的数字放在一起,也可以通过正则表达式获取。
对于剩下的地址,起初是想着暴力解答,通过关键字用正则表达式提取,再用条件判断进行筛选和分析,但是后来发现输入的地址中“省”和“市”等关键字是可以省略的,且关键字太多,无法完全考虑到,对于地址很难进行分割,就放弃了。而后听别的同学再讲用api,然后就临时去学了一下。由于我直接用高德地图api中的地理编码会造成返回数据不全,所以在这个的基础上改用逆地理编码,通过地理编码中的经纬度得到更加全的地址。如果是难度一和难度二,则于输入的地址进行比较,如果不能在输入的地址中找到则该项为空。
函数
函数名称 函数功能 getlevel(s) 获取等级 getname(s1) 获取姓名 gettelphone(s2) 获取电话号码 getaddress(s3) 调用api获取地址 finalget1(s3, position) 对于难度一的地址进行分析 finalget2(s3, position) 对于难度二的地址进行分析 finalget3(s3, position) 对于难度三的地址进行分析
实现过程
- 直接由输入字符串的第一个字符得到等级。
- 通过正则表达式
name = re.match(r'(.+?),', s1)
得到姓名。 - 通过正则表达式
tel = re.search(r'(\d{11})', s2)
得到电话号码,并将这些信息剔除得到只包含地址的字符串。
s3 = s2[:pos[0]] + s2[pos[1]:] #删除手机号码,得到只包含地址的字符串
- 使用高德地图api中的地理编码获得地址的经纬度
url = "https://restapi.amap.com/v3/geocode/geo?output=JSON&key=01a0be09080717e8fa1820123e5d2650&address=%s" % s3 res = requests.get(url) res1 = json.loads(res.text) # 将得到的数据转化为json格式 location = res1['geocodes'][0]['location'] # 得到经纬度
- 由经纬度运用高德地图api中的逆地理编码得到完整的地址
re_url = "https://restapi.amap.com/v3/geocode/regeo?output=JSON&location=%s&key=01a0be09080717e8fa1820123e5d2650&radius=1000&extensions=base" % location re_res = requests.get(re_url) re_res1 = json.loads(re_res.text)
- 根据不同的等级对通过高德地图api得到的数据进行处理,其中由于详细地主没有任何规律,所以需要先将最后的详细地址先提取出来。
#等级一中详细地址的查找 x = re.search(r'.' + township, s3) #township为街道 if x == None: township = "" y = re.search(r'.' + district, s3) #district 为县 if y == None: district = "" z = re.search(r'.' + city, s3) #city 为市 if z == None: city == "" o = re.search(province, s3) pos = o.span() else: pos = z.span() else: pos = y.span() else: pos = x.span() # 寻找s4 s4 = s3[pos[1]:-1] # 输入的末尾存在 "." 得到s4为详细地址 s5 = s3[:pos[1]] # s5为将详细地址取出后的其他地址 #先通过正则找到街道在字符串中的位置,在之后便是详细地址。 #若街道为空则向前寻找县,若仍然为空则再向前找市。 #等级二和等级三的详细地址寻找也类似与等级一,就不列出了。
将api得到的最终地址与原字符串中的地址进行比较,原字符串中若有存在则输出,无则输出空字符串。等级三则直接输出。
难点
- 对于等级一和等级二中,难以去判断api得到该级地址是否有在字符串中(api得到的地址为完整的地址名称,字符串输入的地址可能会省略如“省”、“市”等关键字,正则表达式搜索难以完全匹配),从而难以判断是否输出空字符串。
api中由经纬度返回的地址有时会存在误差,可能匹配到邻近的地址,使得与原本的地址完全不同,从而使输出完全不同。
性能分析
除去input函数,耗时最大的是对高德地图api的调用
def getaddress(s3): url = "https://restapi.amap.com/v3/geocode/geo?output=JSON&key=01a0be09080717e8fa1820123e5d2650&address=%s" % s3 #使用高德地图api获得地址编码 res = requests.get(url) res1 = json.loads(res.text) location = res1['geocodes'][0]['location'] re_url = "https://restapi.amap.com/v3/geocode/regeo?output=JSON&location=%s&key=01a0be09080717e8fa1820123e5d2650&radius=1000&extensions=base" % location #逆地理编码 re_res = requests.get(re_url) re_res1 = json.loads(re_res.text) return re_res1
改进
对于难度一和难度二的地址不需要再次调用高德地图api中的逆地理编码,可以通过地理编码得到的地址用正则表达式和条件判断去判断地址,时间应该会快一点,不过需要考虑的东西太多。
单元测试
单元测试代码
def finalget1(s3, position): # "直辖市/省(省级)","直辖市/市(地级)","区/县/县级市(县级)","街道/镇/乡(乡镇级)","详细地址" province = position['regeocode']['addressComponent']['province'] city = position['regeocode']['addressComponent']['city'] district = position['regeocode']['addressComponent']['district'] township = position['regeocode']['addressComponent']['township'] if province == '北京市' or province == '天津市' or province == '重庆市' or province == '上海市': city = province province = province[:-1] x = re.search(r'.' + township, s3) if x == None: township = "" y = re.search(r'.' + district, s3) if y == None: district = "" z = re.search(r'.' + city, s3) if z == None: city == "" o = re.search(province, s3) pos = o.span() else: pos = z.span() else: pos = y.span() else: pos = x.span() # 寻找s4 s4 = s3[pos[1]:-1] # 输入的末尾存在 "." s5 = s3[:pos[1]] if len(district) == 0: district = "" if len(city) == 0: city = "" if city != "" or city != None: city_test = city[:2] test_c = re.search(city_test, s5) if test_c == None: city = "" a = re.search(district, s5) if a == None: district = "" b = re.search(township, s5) if b == None: township = "" in_location = [province, city, district, township, s4] return in_location
代码说明
对由高德地图api获得的地址进行分析判断,判断该地址中各个级别的地址是否有存在于原字符串,有则保留,无则将该地址设置为空字符串。(难度等级为一)
异常处理说明
部分地址由于经纬度的误差,使得api返回的地址出错。(或者是因为该地方有多个名字,输入的和高德地图的名字不一样,造成返回的地址有误)
样例
输入3!汝舟孙,贵州省修文县龙岗社区15766780930服务中心阳明西路196号龙岗居委会.
输出{"姓名": "汝舟孙", "手机": "15766780930", "地址": ["贵州省", "贵阳市", "修文县", "龙岗社区服务中心", "人民南路", "196号", "龙岗居委会"]}
正确答案{"姓名": "汝舟孙", "手机": "15766780930", "地址": ["贵州省", "贵阳市", "修文县", "龙岗社区服务中心", "阳明西路", "196号", "龙岗居委会"]}