重拾 Python 的学习,二刷了 Learn python the hard way 和廖雪峰老师的教程后,还是有点懵逼。考虑到日常工作常会给出一些逻辑判断相关的需求。就想尝试写一二个,毕竟学以致用。
最近给DT哥哥提了一个从中航信系统的返回内容提取 PNR 的逻辑,先说下 PNR —— Passenger Name Record 旅客订座记录,是系统用于查看所有关于乘客出行信息的唯一性编码,它大概有这些特征:
基本(核心)特征:PNR 一定是个 6 位的、由数字或字母拼的字符组
每一次封口(相当于保存操作)指令必定伴随着一个 PNR 的创建、修改或删除
PNR经常会展示在所在行的最前面,即:PNR是本行第一个字符组的概率很高
PNR有时会展示在返回内容的首行,但不是绝对的,所以只能辅助判断
PNR有时会单独占一行,即:若一行仅有一个字符组且字符组满足PNR的基本特征,那么它是PNR的概率很高
返回内容里很可能存在长度为6位但不是PNR的内容,即出发到达机场三字码的组合(比如PVGKIX代表上海到大阪),但幸运的是这种情况我们是比较容易排除的,比如三字码组的后方肯定是行动代码,而行动代码的格式是固定的
有一定的概率,返回内容会展示一行RT: PNR 的数据,即:当一行首个字符组是RT,第二个字符组符合PNR基本特征,那么它是编码的概率极高
编码是少有的可能在返回内容的不同行重复出现的,即:若返回内容里有多个符合PNR特征的相同字符组,那么该字符组是PNR的可能性进一步加高
如上文所述,封口对应的 PNR 操作可能有创建、修改、删除。我暂且先选择了创建时的场景进行识别,其程序逻辑描述大概有:
以空格为间隔符,查找返回内容每行的独立字符组,选取其中长度为6位由数字或字母的字符组,作为PNR待选项,设置每个符合该基本特征的字符组得分 1
若待选字符组是本行第1个字符组,则得分 +2 ,若该字符组行内下一个字符组为 '-'或'-EOT'或空则再 +1
若待选字符组是本行第2个字符组且第一个字符组为'RT'或'RT:',则得分 +3
若待选字符组下一个字符组是行动代码(格式为HK/DK/HL/HN+数字),则得分 -3
将相同待选字符组的得分相加,选择得分最高的作为 PNR 可能选项(后面还有一些其他选项不过都不是重点了)
说干就干,核心思路是利用正则进行提取和判断,我将多个样例内容存放在一个txt方便判断逻辑面对不同内容的识别准确度,为了查看文件,还需要用到 IO(用了比较省心的with open..)
import re #导入正则表达式模块
pnrlines = [] #定义文本行信息
with open('pnr.txt','r') as pnrtext: #打开待识别的txt文本
pnrlines = pnrtext.readlines() #提取每一行,去掉首位空格后赋值给行信息
通过readlines按行读取(“行”的概念后面要用到所以这里这样考虑了),并定义了后面会用到的各种特征的识别函数(符合基本特征的编码、RT、行动代码):
def getPnr(lineText): #定义根据空格或换行切分字符组的函数
get_pnr = re.split(r'[\s\n]+',lineText.strip())
return get_pnr
def findPnrs(lineText2): #定义判断条件:str是否为6位字母+数字,若是返回该值
if re.match(r'^([0-9a-zA-Z]{6})$',lineText2):
find_pnrs = re.match(r'^([0-9a-zA-Z]{6})$',lineText2).group()
elif re.match(r'^(HK|DK|HN)[0-9]{1}$',lineText2):
find_pnrs = 'PreNone'
elif re.match(r'^(RT|RT:)$',lineText2):
find_pnrs = 'NexPNR'
else:
find_pnrs = 'none'
return find_pnrs
轮询每行的每个字符,进行对字符的特征进行识别,标记其属性(待选项、RT、行动代码)并将打分预置
i = 0
for i in range(len(pnrlines)): #循环每一行
this_line = pnrlines[i]
pnrlist = getPnr(this_line)
print('第%d行:\n'%(i+1))
print('this_line: ',this_line)
print('pnrlist: ',pnrlist,'\n')
p = 0
q = p + 1
likePNR = []
PNRCODE = {}
defaultPsb = 0
for p in range(len(pnrlist)): #循环行内每个切分出的字符组
thisPnr = pnrlist[p]
pnrs = findPnrs(thisPnr) #判断字符组是否满足条件(6位字母+数字)
#print('NO.',p+1,': ',thisPnr,' | 结果: ',pnrs) #打印每个字符组的判断结果
likePNR.append(pnrs)
PNRCODE[pnrs] = 0
p += 1
#print('pnrcheck: ',likePNR)
if pnrs not in ('none','NexPNR','PreNone'):
psb = defaultPsb + 1
elif pnrs == 'NexPNR':
False
else:
psb = defaultPsb
PNRCODE[pnrs] = psb
PNRCODE['none'] = 0 #强行为dict增加一个none
del PNRCODE['none'] #删除dict里的none
print('PNRCODE: ',PNRCODE)
print('\n')
i += 1
运行后,得到了一个结果,当然这只是个中继,还没有判断加分项:
未完待续..