coding-coworking-club / python-spring-2021

ccClub Python Spring 2021
https://www.ccclub.io/course/2021Spring
23 stars 6 forks source link

[Homework 4] 公平抽獎 #496

Closed jeje898281 closed 3 years ago

jeje898281 commented 3 years ago

提交連結

https://judge.ccclub.io/status/28e0ecc43b773ef0ce4df89b56deb357

程式碼

import re
import datetime

# 抽獎券後面要填的欄位包括 姓名、身分證字號、性別、出生年月日、年齡、市話、手機、電子信箱

def check(string):
    def id_check(id_num):
        if dic["sex"] == "男" and id_num[1] != "1":
            return False
        if dic["sex"] == "女" and id_num[1] != "2":
            return False
        s = id_num[0]
        if s == "B" or s == "N" or s == "Z":
            n1 = 0
        elif s == "A" or s == "M" or s == "W":
            n1 = 1
        elif s == "K" or s == "L" or s == "Y":
            n1 = 2
        elif s == "J" or s == "V" or s == "X":
            n1 = 3
        elif s == "H" or s == "U":
            n1 = 4
        elif s == "G" or s == "T":
            n1 = 5
        elif s == "F" or s == "S":
            n1 = 6
        elif s == "E" or s == "R":
            n1 = 7
        elif s == "D" or s == "O" or s == "Q":
            n1 = 8
        elif s == "C" or s == "I" or s == "P":
            n1 = 9
        else:
            return False

        others = int(id_num[1]) * 8 + int(id_num[2]) * 7 + int(id_num[3]) * 6 \
                 + int(id_num[4]) * 5 + int(id_num[5]) * 4 + int(id_num[6]) * 3 \
                 + int(id_num[7]) * 2 + int(id_num[8]) * 1 + int(id_num[9]) * 1

        if (n1 + others) % 10 == 0:
            return True
        else:
            return False

    def isValidDate(birthday, before=False):
        birthday = birthday.split("/")
        if before:
            year = -int(birthday[0]) + 1911
        else:
            year = int(birthday[0]) + 1911
        month = int(birthday[1])
        day = int(birthday[2])
        try:
            a = datetime.date(year, month, day)
            return True
        except:
            return False

    def age_check(age, birthday, before=False):
        a = birthday.split("/")
        if not before:
            birthday_format = datetime.date(year=int(a[0]) + 1911, month=int(a[1]), day=int(a[2]))
        else:
            birthday_format = datetime.date(year=(-int(a[0])) + 1911, month=int(a[1]), day=int(a[2]))
        today = datetime.date(year=2021, month=4, day=24)
        right_age = int((today - birthday_format).days // 365.25)
        if age != right_age:
            return False
        if age > 122:
            return False
        return True

    def phone_check(phone):
        if phone[phone.index("(")+1:phone.index(")")] not in ["02", "03", "037", "04", "049", "05", "06", "07", "08", "089",
                                                            "082", "0826", "0836"]:
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "02" and len(phone[phone.index(")")+1:phone.index("-")]) != 4:
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "03" and len(phone[phone.index(")")+1:phone.index("-")]) != 3:
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "037" and len(phone[phone.index(")")+1:phone.index("-")]) != 2:
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "04" and \
                (len(phone[phone.index(")")+1:phone.index("-")]) != 4 and
                 len(phone[phone.index(")")+1:phone.index("-")]) != 3):
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "049" and len(phone[phone.index(")")+1:phone.index("-")]) != 3:
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "05" and len(phone[phone.index(")")+1:phone.index("-")]) != 3:
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "06" and len(phone[phone.index(")")+1:phone.index("-")]) != 3:
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "07" and len(phone[phone.index(")")+1:phone.index("-")]) != 3:
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "049" and len(phone[phone.index(")")+1:phone.index("-")]) != 3:
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "08" and len(phone[phone.index(")")+1:phone.index("-")]) != 3:
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "089" and len(phone[phone.index(")")+1:phone.index("-")]) != 2:
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "082" and len(phone[phone.index(")")+1:phone.index("-")]) != 2:
            return False
        if phone[phone.index("(")+1:phone.index(")")] == "0836" and len(phone[phone.index(")")+1:phone.index("-")]) != 1:
            return False
        return True

    def mail_check(mail_name):
        address_time = mail_name.count("@")
        if address_time != 1:
            return False
        first_part = mail_name.split("@")[0]
        second_part = mail_name.split("@")[1]
        if not first_part or not second_part:
            return False
        w = []
        for i in first_part:
            if i not in "+-._0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
                return False
            if not w:
                if i in "+-._":
                    return False
                else:
                    w.append(i)
            elif w[-1] in "+-_.":
                if i in "+-_.":
                    return False
                else:
                    w.append(i)
            else:
                w.append(i)
        e = []
        for i in second_part:
            if i not in ".0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
                return False
            if not e:
                if i == ".":
                    return False
                else:
                    e.append(i)
            elif e[-1] == ".":
                if i == ".":
                    return False
                else:
                    e.append(i)
            else:
                e.append(i)
        if e[-1] == ".":
            return False
        if "." not in e:
            return False
        return True

    regex_name = r',[\u4e00-\u9fff|.]{2,},'
    regex_id = r',[A-Za-z][12]\d{8},'
    regex_sex = r',[男女]{1},'
    regex_birth = r',\d{1,3}/\d{2}/\d{2},'
    regex_age = r',\d{1,3},'
    regex_phone = r',\(\d{2,4}\)\d{1,4}-\d{4},'
    regex_ceil = r',09\d{8},'
    regex_email = r',[a-zA-Z0-9_.+-]+@[a-zA-Z0-9.]+,'
    regex_birth_before = r',民前\d{1,3}/\d{1,2}/\d{1,2},'

    match_name = re.findall(regex_name, string)
    match_id = re.findall(regex_id, string)
    match_sex = re.findall(regex_sex, string)
    match_birth = re.findall(regex_birth, string)
    match_age = re.findall(regex_age, string)
    match_phone = re.findall(regex_phone, string)
    match_ceil = re.findall(regex_ceil, string)
    match_email = re.findall(regex_email, string)
    match_birth_before = re.findall(regex_birth_before, string)

    dic_gentle_ladies = {"男": "先生", "女": "小姐"}
    dic = {}
    if len(match_name) == 1:
        dic["name"] = match_name[0][1:-1]
    if len(match_id) == 1:
        dic["id"] = match_id[0][1:-1]
    if len(match_sex) == 1:
        dic["sex"] = match_sex[0][1:-1]
    if len(match_birth) == 1:
        dic["birth"] = match_birth[0][1:-1]
    if len(match_age) == 1:
        dic["age"] = match_age[0][1:-1]
    if len(match_phone) == 1:
        dic["phone"] = match_phone[0][1:-1]
    if len(match_ceil) == 1:
        dic["ceil"] = match_ceil[0][1:-1]
    if len(match_email) == 1:
        dic["email"] = match_email[0][1:-1]
    if len(match_birth_before) == 1:
        dic["birth_before"] = match_birth_before[0][3:-1]

    if "name" not in dic or "id" not in dic or "sex" not in dic or ("birth" not in dic and "birth_before" not in dic) \
            or "age" not in dic or ("phone" not in dic and "ceil" not in dic and "email" not in dic):
        return False
    if not id_check(dic["id"]):  # 開始檢查流程,首先檢查身分證
        return False
    if "birth" in dic:
        if not isValidDate(dic["birth"]):  # 檢查生日日期合法
            return False
    if "birth_before" in dic:
        if not isValidDate(dic["birth_before"], True):  # 檢查民前生日日期合法
            return False
    if "birth" in dic:
        if not age_check(int(dic["age"]), dic["birth"]):
            return False
    if "birth_before" in dic:
        if not age_check(int(dic["age"]), dic["birth_before"], True):
            return False
    if "phone" in dic:
        if not phone_check(dic["phone"]):
            return False
    if "email" in dic:
        if not mail_check(dic["email"]):
            return False
    return [dic["age"]+"歲", dic["name"]+dic_gentle_ladies[dic["sex"]], "身分證末三碼"+dic["id"][-3:]]

answers = []
o = input()
while o != "END":
    ans = check("," + o + ",")
    if ans:
        if ans not in answers:
            answers.append(ans)
    o = input()
for answer in answers:
    print(" ".join(answer))

錯誤訊息

Wrong Answer

問題描述

很抱歉,這題程式碼打的有點冗長,下面分段來說解題的思路可能會比較易讀。範例的測資都是正確的,有自己嘗試抓蟲除錯蠻多次,但都是還是WA,不太確定問題是出在哪個部分,先謝謝助教的指導。

一、架構 我的解題整體架構是用while迴圈的方式,把每次輸入的一行字串丟進check函式做處理,如果資料符合要求,那麽就存入一個answers列表,如果資料不符合規則,就不存入,繼續讀入下一筆資料並讀取,直到輸入的資料為end就跳出迴圈,並按格式輸出answers列表中每個抽獎券。

二、主要函式

  1. 因為輸入的格式是以逗號分隔參與者的所有資料,為了後續方便套件re正確抓出想要的資料,所以將字串前後都加上逗號,確保參與者的所有資料是被兩個逗號包住的。
  2. 接著我利用re套件,分別抓出姓名(中文的部分不知道這樣抓是否確)、身分證、性別、生日(民國前後分別處理)、年齡、及電話等參與者的資料,並用re.findall的功能,分別抓取所有符合的資料。
  3. 接著我用if判斷每個抓取到的列表裡是否正好有一個,如果有的話就去掉頭尾的逗號並加入字典dic裡,以利後續處理。
  4. 根據資料規則,抽獎券上面的空格,除了市話、手機和電子信箱可以擇一填寫之外,都不能空白,因此用if判斷是否符合,如果有該有的資料沒有就回傳false。

三、主要函式中的次要檢查函式

  1. 再來是因為利用re套件只是抓取資料,有些在抓取時已經確保資料的合法性如手機號碼,但大多數還未確認資料的合法性,因此將需要檢查合法性的資料丟進各個檢查函式檢查,並根據回傳值,若非檢查函式True則主函式會終止並回傳False。以下說明各個檢查函式
  2. 首先檢查身分證:首先判斷此筆資料為男性或女性,若與身分證性別碼不相符則回傳False,再來按照身分證規則把位數加權後除以10,若不能整除則回傳False
  3. 接著檢查生日日期(包括民前與民國後)是否合法,在isValidDate的函式中,先判斷是否為民前的(如果是民前要加上負號),再來將民國年份加上1911來表示西元年份,接著用try和except並搭配datetime模組的方法來檢查日期是否是合法的,如果非法則回傳False
  4. 接著檢查生日與年齡,以活動當天2021/4/24為準,利用datetime套件並以一年265.25天計算,檢查生日與參與者填寫的年齡是否相符,如果不符回傳False。除此之外,還檢查年齡是否超過金氏世界紀錄最大值122歲,如超過也回傳False
  5. 接下來檢查家用電話號碼,根據中華民國長途電話區號表的規則,括號內的數字必須在一定的數字組合中,而且根據括號內不同的數字組合代表不同的區域,後面的數字碼也有不同的規定,其中檢查的規則是根據在維基百科查詢到的規則,如果不符規則回傳False。
  6. 接著檢查電子郵件,根據作業四第一題的規則,進行檢查(那題有AC,將程式碼轉為函式來使用於此)。

四、輸出結果 如果所有資料皆通過以上檢查,那麼將資料整理成抽獎券的格式,並且儲存近answers裡,最後輸出。

還希望助教能糾正我,找到錯誤的地方,也先謝謝看冗長的程式碼

hchbiggrass commented 3 years ago

你很認真,很棒。這一題的測資主要是錯在電子信箱的部分,如果你依照「信箱地址檢查」那一題的程式來檢查這一題的信箱地址的話應該就會過了。不過建議你使用正規表達式(re)來解題,試試看用正規表達式直接從一團字串裡面找到想要的字串,你會驚訝地發現程式碼的行數變少很多。

jeje898281 commented 3 years ago

@hchbiggrass 謝謝助教的回覆指導,我將信箱處理的方式更改過,除了在抓取的部分修改正規表示法跳脫字元處理錯的地方,檢查的函式也改成完全是依照「信箱地址檢查」那一題通過的程式碼來檢查這一題的信箱地址,但目前還是WA,想請問助教我是不是還有一些其他地方是沒有考慮到的,還是信箱的處理方式仍然有誤,謝謝助教! 新繳交的程式碼連結:https://judge.ccclub.io/status/fdd82b42254a1dc0f8184f37b3de5be3

hchbiggrass commented 3 years ago

錯誤的模式還是一樣,你試試看這兩筆測資,都判斷不合格才對 lk=jd@gmail.com,53,0912103541,I143858601,,傅明輝,56/06/05,男 (082)41-5964,鄧詩涵,28,81/12/15,0915055698,lkjd@gm_ail.com,女,C255081381

jeje898281 commented 3 years ago

@hchbiggrass 真的很感謝助教!我發現問題是我沒有考慮到雖然電子郵件、電話和手機可以擇一填,但假如有多填上去的資料,也都要檢查是否正確的,修正後已經AC了!謝謝助教!

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.