🐸

报名单清洁师

数据与文本工具python-data-tinkerer-38-the-signup-cleaner
奖励: 160 XP
|

报名单清洁师

活动临近了,负责现场签到的协作者正等一份能直接核对的名单。可你手里的报名表还不够放心:有人多打了空格,有人重复提交,有的行甚至缺了关键信息。要是现在就交出去,现场的人还得一边接待一边人工猜哪条才算有效报名。

所以你现在要做的事很明确:把这份小小的 CSV 名单清干净,交给签到台和后续统计的人继续用。你会清理字段、跳过关键信息空白的记录、按邮箱保留第一份报名,再整理出一份可直接使用的名单。

先热身:把一条报名记录收整齐

先别急着碰整张表。先看一个玩具例子,感受一下最小动作:一条脏报名记录,怎么先被拆开,再被收成稳定字段。

row = "  Ava Stone  , AVA@example.com  , vip "
full_name, email, ticket_type = row.split(",")

clean_full_name = full_name.strip()
clean_email = email.strip().lower()
clean_ticket_type = ticket_type.strip().lower()

print(clean_full_name)
print(clean_email)
print(clean_ticket_type)

这里还没有处理整份名单:没有跳过空白,也没有按邮箱去重。它只是在示范今天最关键的第一步——先把一行拆开,再把真正要交出去的字段收整齐。

今天要交付什么:把 signup_sheet.csv 变成现场可用名单

脚本已经读好了 signup_sheet.csv,并把表头放进 header_line,把真正要处理的数据行放进 dirty_rows。你要补完脚本,让它做出两份结果:一份给人继续用的名单,一份说明清理情况的摘要。

1
先完成单行清理函数

完成 clean_signup_row(row)。用 row.split(",") 拆出 full_nameemailticket_type,再用 strip() 清理字段边缘,并把 emailticket_type 变成小写。

2
遇到空白关键字段就跳过

如果清理后的 full_nameemailticket_type 还是空的,就让函数返回 None。主循环会把这种记录计入 skipped_blank_count

3
按清理后的邮箱去重,保留第一次出现的报名

seen_emails 这个 set 记住已经保留过的邮箱。只要某个清理后的邮箱已经出现过,就把它记进 duplicate_count,不要重复收进最终名单。

4
输出可用名单和总结信息

把保留下来的 dict 放进 usable_signups,再重组成可直接交付的 CSV 行,收进 usable_rows。最后用 signup_summary 说明总行数、保留数量、空白跳过数量和重复数量。

交付标准很简单

这份名单不需要花哨,只要下一位协作者一看就能继续用:字段整齐、空白记录不混进来、重复邮箱不会被重复计算。

参考答案
点击展开
参考答案:
with open("signup_sheet.csv", "r", encoding="utf-8") as file:
  lines = file.read().splitlines()

header_line = lines[0]
dirty_rows = lines[1:]

print("Header:", header_line)
print("Dirty rows:", dirty_rows)


def clean_signup_row(row):
  full_name, email, ticket_type = row.split(",")
  clean_full_name = full_name.strip()
  clean_email = email.strip().lower()
  clean_ticket_type = ticket_type.strip().lower()

  if clean_full_name == "" or clean_email == "" or clean_ticket_type == "":
      return None

  return {
      "full_name": clean_full_name,
      "email": clean_email,
      "ticket_type": clean_ticket_type,
  }


usable_signups = []
seen_emails = set()
usable_rows = [header_line]
skipped_blank_count = 0
duplicate_count = 0

for row in dirty_rows:
  cleaned_signup = clean_signup_row(row)

  if cleaned_signup is None:
      skipped_blank_count += 1
      continue

  cleaned_email = cleaned_signup["email"]

  if cleaned_email in seen_emails:
      duplicate_count += 1
  else:
      seen_emails.add(cleaned_email)
      usable_signups.append(cleaned_signup)
      usable_row = (
          cleaned_signup["full_name"]
          + ","
          + cleaned_signup["email"]
          + ","
          + cleaned_signup["ticket_type"]
      )
      usable_rows.append(usable_row)

signup_summary = {
  "raw_row_count": len(dirty_rows),
  "kept_signup_count": len(usable_signups),
  "skipped_blank_count": skipped_blank_count,
  "duplicate_count": duplicate_count,
}

print("Usable signups:", usable_signups)
print("Usable rows:", usable_rows)
print("Signup summary:", signup_summary)
高级技巧
想更进一步?点击展开

这节课最重要的变化是:你整理出来的不再只是“练习里的字符串结果”,而是一份现场真的能继续使用的名单。

当字段清理、空白判断、去重和结果整理连成一条线时,你已经在做一件很像真实协作的小工具任务了。

Loading...
终端输出
Terminal
Ready to run...