最近有學習到 正則表達式,有一點收穫,分享一下;
re.search()
情景A
某需求中 銀行流水的description字段值是我們財務同事來填寫的,我想給這條流水來匹配某些關鍵字,咋搞?
我最初的思路就是 description字段值.find(關鍵字);
管你三七二十一,find()的結果 非-1,就代表能找到;
加深一點,如果某些關鍵字 如下圖
onnqor&SYAFTRACO 這個關鍵字, & 不是內容,是and ; 整個關鍵字 實際理解爲 【onnqor 某些內容 SYAFTRACO】;
故不能用find();
def find_str(self, description, keyword):
"""
非標匹配
:param description:
:param keyword:
:return:
"""
# 會先進行普通規則匹配【匹配時 不改變description】,匹配失敗纔會進行正則匹配
desc = description.replace(' ', '')
# 實際非標匹配是有 & 和 ...
k_str = keyword.replace('&', '.*').replace('...', '.*')
if (re.search(k_str, desc)) is not None:
Log.info('當前元素 {},正則匹配成功 {}'.format(description, keyword))
情景B
假設 某銀行流水的details字段的 ref_no 是我們財務同事來填寫的,要來匹配某些規則(前綴爲某關鍵字 + 結尾爲12位數字【年月日 8位數字+4位編號】),流水的details字段值 如下圖 【爲了不泄密,就是這樣的啦】
def check_json(self, j):
try:
json.loads(j)
return True
except BaseException:
return False
def find_str_2(self, data_list):
zz_1 = re.compile('^{"')
zz_2 = re.compile('\d{12}$')
for data in data_list:
detail = data[-1]
if detail is not None and zz_1.search(detail) is not None and self.check_json(
detail) is True and ('ref_no' in json.loads(detail).keys()) is True:
reference = json.loads(detail)['ref_no']
# Log.info(reference)
yuanzu = ('探親', '出差', '薪資(CN)', 'zyooooxie', 'csdn')
if reference is not None and reference.startswith(yuanzu) is True and zz_2.search(reference.strip()) is not None:
Log.info('將進行匹配')
變量yuanzu 代表了 所有的前綴關鍵字;
而結尾的12位數字,應該要再優化下【思路是 寫死 年月日的取值範圍,從20200101開始】
def end_search(self, reference):
"""
編號的日期 是 20200101開始,後四位爲 全數字
:return:
"""
zz_2 = re.compile(r'\d{12}$')
print(zz_2.search(reference), '優化前')
test_str = '(20[2-9]\d|[3-9]\d{3}|2[1-9]\d{2})(0[1-9]|1[1-2])(0[1-9]|[1-2]\d|3[0-1])\d{4}$' # 年份大於等於2020,小於等於9999
zz_2_new = re.compile(test_str)
print(zz_2_new.search(reference), '優化後')
好像不錯呦;
但 我傳 20200431(4月沒有31號),用上面的正則 是校驗不出來的;
說起來 每月的天數 30or31or29or28,校驗是很複雜的 (如2月29 -> 閏年的’四年一閏,百年不閏,四百年再閏’ ),我只能 再寫個校驗日期的方法 = = (實際原因:正則表達式,我實在是太菜,搞不定)
def check_date(self, reference, zz_2_new):
# test_str = '(20[2-9]\d|[3-9]\d{3}|2[1-9]\d{2})(0[1-9]|1[1-2])(0[1-9]|[1-2]\d|3[0-1])\d{4}$' # 年份大於等於2020,小於等於9999
# zz_2_new = re.compile(test_str)
if zz_2_new.search(reference) is None:
return False
else:
result = zz_2_new.search(reference).group()
check_date = result[0:8]
check_month = result[0:6]
f_l = self.month_first_last('-'.join([check_month[0:4], check_month[4:]]))
check_date = '-'.join([check_date[0:4], check_date[4:6], check_date[6:]])
try:
assert check_date in self.date_list(f_l[0], f_l[1])
return True
except AssertionError:
return False
定義yuanzu變量後 代碼改爲:
# 前面的zz_2
test_str = '(20[2-9]\d|[3-9]\d{3}|2[1-9]\d{2})(0[1-9]|1[1-2])(0[1-9]|[1-2]\d|3[0-1])\d{4}$' # 年份大於等於2020,小於等於9999
zz_2 = re.compile(test_str)
yuanzu = ('探親', '出差', '薪資(CN)', 'zyooooxie', 'csdn')
if reference is not None and reference.startswith(yuanzu) is True and self.check_date(reference.strip(), zz_2) is True:
pass # 以下省略
實際的困境
以上 是我寫的匹配過程,但我們後臺的思路更粗暴:直接把reference的字段值 扔到某個庫表去匹配all結果,匹配上,就ok;匹配不上,匹配結束;
實際很不符合需求!
產品小姐姐 定的需求是 匹配前先檢查reference是否符合這一類規則,符合就走這一類匹配,不然就走 情景A說的關鍵字匹配;只是 季度末趕時間交付,產品大佬 又改需求,so 我也得改成這樣粗暴匹配;
來到這兒,就得考慮實際庫表的記錄數量,之前分享過:Python的dict來處理大數據量對賬 ,故 把 庫表all值 某個字段做成dict
def oa_table_llbh_dict(self):
new_data = self.OA_VALUE()
# Log.info(new_data)
new = [i[0] for i in new_data]
new_dict = dict.fromkeys(new, True)
# Log.info(new_dict)
return new_dict
這樣的情景下,代碼變成:
yuanzu = ('探親', '出差', '薪資(CN)', 'zyooooxie', 'csdn')
if (reference.strip() if reference is not None else reference) in self.oa_table_llbh_dict().keys():
pass # 以下省略
情景C
情景:某文件夾內,有很多Excel、CSV文件,其中CSV都是相關Excel轉換來的【假設名稱完全一樣,除了格式不同】;我想知道某個Excel有沒有轉換成CSV文件,咋整?
思路好像就是: 列出整個文件夾的內容,進行查找,若找到就return,找不到就拉倒;
def find_excel_csv(self, file):
import os
import re
os.chdir(os.path.dirname(file))
# Log.info(file)
# Log.info(os.getcwd())
all_file = os.listdir('.')
Log.info(all_file)
file_csv = '^{}.*.csv$'.format(os.path.basename(file)[:-5])
Log.info(file_csv)
for a in all_file:
if re.search(file_csv, a) is not None:
Log.info('找到了')
Log.info(a)
return a
上面的 for某循環 if某條件,可以直接寫成 列表生成式 【或是 列表推導式,列表解析式】 之前有說到 :Python 筆記【一】列表生成式
def find_csv(self, file):
import os
os.chdir(os.path.dirname(file))
all_file = os.listdir('.')
file_csv = '^{}.*.csv$'.format(os.path.basename(file)[:-5])
Log.info(file_csv)
ele = [a for a in all_file if re.search(file_csv, a) is not None]
if len(ele) != 0:
Log.info('找到了')
new_file = ele[0]
Log.info(new_file)
else:
Log.info('整個文件夾沒有相關的CSV文件')
交流技術 歡迎+QQ 153132336 zy
個人博客 https://blog.csdn.net/zyooooxie