我想透過VBA找出儲存格之間的關係
目前有一堆這樣的資料
每日組粗框代表他是同一組,但是在A欄中會發現123456出現了"兩組"分別在A2:A6&A22,第一組(A2:C6)是同一人,同月份,2種或多種假期類別,假期申放時間先連續後單日,第二組(A22:C22)同一人同月份,1種假期類別,假期申放時間單日。
我如何透過VBA來判斷儲存格之間關係並生成新資訊?
如下圖,我在A欄輸入上圖的員工號碼,生成B欄的結果,結果我會用來改檔案名稱.
其實有點複雜,所以請各大神幫幫忙,跪謝!
這裡提供您一個使用Python 土法鍊鋼的方式,首先你需要把要處理的工作表匯出成txt文字檔(tab分隔),
這部份很簡單,就是開啟excel,然後點 "檔案→另存新檔",檔案類型選 "文字檔(tab字元分隔)",然後存成 "假期分類.txt"(可用Excel開啟)
底下是我測試時的 "假期分類.txt" 內容:
員工號碼 開始日期 假期種類 備註
123456 1/1/2020 年假 1天
123458 27/1/2020 年假 1天
123456 2/1/2020 年假 1天
123456 7/1/2020 特休 1天
123457 9/1/2020 年假 1天
123457 12/1/2020 年假 1天
123458 20/1/2020 年假 1天
123456 3/1/2020 年假 1天
123458 25/1/2020 年假 1天
123458 26/1/2020 年假 1天
123458 28/1/2020 年假 1天
123456 4/1/2020 年假 1天
123458 29/1/2020 年假 1天
123458 30/1/2020 年假 1天
123458 31/1/2020 年假 1天
123458 1/2/2020 年假 1天
123456 8/2/2020 病假 1天
123460 28/1/2020 事假 1天
再來就是使用Python的部份,你需要先到Python官網下載適合自己OS版本的Python安裝程式,建議安裝3.7以上版本(我目前是用3.7.9),安裝好之後開個cmd視窗打python[enter]如果出現的是下列內容就表示安裝成功,可正常執行Python了。
Python 3.7.9 (tags/v3.7.9:13c94747c7, Aug 17 2020, 18:58:18) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>
接著直接關掉cmd視窗,看一下python程式的部份(分析處理 "假期分類.txt" 文字檔 並將結果另存為 "假期分類_完成.txt")
main.py
import datetime
""" 逐行讀取文字檔並傳回陣列 """
def read_file_into_list(file_loc,write_mode="r",encode_set="utf-8",split_char="\n"):
tmp_list=open(file_loc,write_mode,encoding=encode_set).read().split(split_char)
out_list=[]
for i in tmp_list:
i_list=i.split("\t")
if len(i_list)==4 and i_list[1].find("/")>-1:
#將日期格式調為"年月日"(目前是"日月年")
date_split=i_list[1].split("/")
i_list[1]=date_split[2]+"/"+fix_date_0(date_split[1])+"/"+fix_date_0(date_split[0])
out_list.append(i_list)
return out_list
""" 將陣列元素逐行寫入文字檔 """
def write_file_from_list(file_loc,tmp_list=[],write_mode="w+",encode_set="utf-8"):
if len(tmp_list)!=0:
with open(file_loc,write_mode,encoding=encode_set) as f:
for i in tmp_list:
f.writelines(i+'\n')
f.close()
print("[ OK ] 陣列寫入文字檔 "+get_file_fullname(file_loc)+" 完成! 文件編碼:"+encode_set+"")
else:
print("[ XX ] 陣列寫入文字檔 "+get_file_fullname(file_loc)+" 失敗!")
return False
""" 取得完整檔名(不含路徑) """
def get_file_fullname(input_str1):
return input_str1.split("\\")[-1]
def fix_date_0(arg):
return arg.strip().rjust(2,"0")
def day_of_year(day_str):
return datetime.date(int(day_str[0:4]),int(day_str[5:7]),int(day_str[8:10])).timetuple().tm_yday
def get_weekday(day_str):
return datetime.date(int(day_str[0:4]),int(day_str[5:7]),int(day_str[8:10])).weekday()
def reduce_d_str(tmp_str):
return tmp_str[0:10]+"~"+tmp_str[-5:] if len(tmp_str)>10 else tmp_str
def print_row(tmpid,tmp_days,tmp_events):
if tmp_days.find(',')<0: # 只有一個日期
return tmpid+'\t'+tmpid+' '+tmp_events+' '+tmp_days+'\t'+'同一人同月份,1種假期類別,申請時間單日'
else:
tmp_events_list=tmp_events.split(',')
events_collection_list=[]
for i in tmp_events_list:
if i not in events_collection_list: events_collection_list.append(i)
#print(tmp_events_list)
tmp_days_list=tmp_days.split(',')
#print(tmp_days_list)
#把休假日期跟種類轉成陣列 用來進行分析 例如
if len(events_collection_list)==1: #只有一個種類
month_collect=[]
for i in tmp_days_list:
#抓取月份字串,如果month_collect內沒有就把它放入moth_collect
tmp_mon=i[6:8]
if tmp_mon not in month_collect:
month_collect.append(tmp_mon)
mon_str="同月份"
if len(month_collect)>1:
mon_str="不同月份"
return tmpid+'\t'+tmpid+' '+events_collection_list[0]+' '+reduce_d_str(tmp_days)+'\t'+'同一人'+mon_str+',1種假期類別,申請時間連續多個單日'
else:
result_txt=""
month_collect=[]
for i in events_collection_list:
if result_txt!="":
result_txt+=","
result_txt+=i
tmp_txt=""
for j in range(0,len(tmp_events_list)):
#抓取月份字串,如果month_collect內沒有就把它放入moth_collect
tmp_mon=tmp_days_list[j][6:8]
if tmp_mon not in month_collect:
month_collect.append(tmp_mon)
if tmp_events_list[j]==i:
if tmp_txt!="":
tmp_txt+=","
tmp_txt+=tmp_days_list[j]
if tmp_txt.find(",")!=-1:
tmp_txt=reduce_d_str(tmp_txt)
result_txt+= tmp_txt
mon_str="同月份"
if len(month_collect)>1:
mon_str="不同月份"
result_txt+="\t同一人"+mon_str+","+str(len(events_collection_list))+"種假期類別,申請時間連續多個單日"
return tmpid+'\t'+tmpid+' '+result_txt
if __name__ == "__main__":
source_txt="假期分類.txt"
final_txt="假期分類_完成.txt"
# 讀取"假期分類.txt"並轉成陣列
list_source=read_file_into_list(source_txt,encode_set="Big5")
result_list=[]
result_list.append("員工號碼 \t 生成結果 \t 解釋")
id_list=[]
for i in list_source:
if i[0] not in id_list:
id_list.append(i[0])
#print('用戶編號 陣列 : ',id_list,'\n')
#用日期欄做排序
list_source=sorted(list_source,key=lambda x: x[1])
day_box=[]
event_box=[]
for i in id_list:
current_id=i
tmp_row=[]
for j in list_source:
if j[0]==current_id:
tmp_row.append(j)
#print('userid: ',i," TotalRow: ",len(tmp_row))
days_str=""
event_str=""
last_date=""
for k in range(0,len(tmp_row)):
#print(tmp_row[k])
if days_str=="": #第一筆
last_date=tmp_row[k][1]
days_str+=tmp_row[k][1]
event_str+=tmp_row[k][2]
elif day_of_year(last_date)+1==day_of_year(tmp_row[k][1]) or (get_weekday(last_date)==5 and get_weekday(tmp_row[k][1])):
days_str+=","+tmp_row[k][1]
event_str+=","+tmp_row[k][2]
last_date=tmp_row[k][1]
else:
result_list.append(print_row(i,days_str,event_str))
#print(i+'\t'+reduce_d_str(days_str),"::",event_str)
days_str=tmp_row[k][1]
event_str=tmp_row[k][2]
last_date=tmp_row[k][1]
#最末筆
result_list.append(print_row(i,days_str,event_str))
#print("-----------")
#將 result_list 轉存為 txt
write_file_from_list(final_txt,result_list,encode_set="Big5")
執行結果:
"假期分類_完成.txt"內容
員工號碼 生成結果 解釋
123456 123456 年假2020/01/01~01/04,特休2020/01/07 同一人同月份,2種假期類別,申請時間連續多個單日
123456 123456 病假 2020/02/08 同一人同月份,1種假期類別,申請時間單日
123458 123458 年假 2020/01/20 同一人同月份,1種假期類別,申請時間單日
123458 123458 年假 2020/01/25~02/01 同一人不同月份,1種假期類別,申請時間連續多個單日
123457 123457 年假 2020/01/09 同一人同月份,1種假期類別,申請時間單日
123457 123457 年假 2020/01/12 同一人同月份,1種假期類別,申請時間單日
123460 123460 事假 2020/01/28 同一人同月份,1種假期類別,申請時間單日
以下是參考源碼下載:
http://www.web3d.url.tw/ITHELP/py_xls_txt_analyzer.zip
對了,編輯.py文件我個人推薦 Notepad++ 免費好用佔用資源少
Excel能處理的話就直接在Excel處理自然比較省事,但是有時如果條件設太複雜用Excel內建功能會變得更難,以這邊的例子用Python處理就簡單很多(當然你設的條件愈複雜,在程式裡就要花更多工夫去處理這個不管是Python或VBA都是一樣的)。
一般常見的python excel處理會用到pandas套件,考慮到樓主可能還沒碰過Python,我還是先把問題單純化,從最基本的文字檔讀入python轉陣列,在python中跑廻圈作條件判斷,再將結果另存文字檔。
等你熟悉Python的程式之後,一些例行的工作也可以弄成自動化處理(例如寫一個robot,把每天例行工作寫成十個腳本,每天固定時間去執行它,然後等下班._." sorry扯遠了)
總之,希望對您的問題有一些幫助。