尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
freeCodeCamp Python 每日编程挑战 48:用正则与数字规则实现 Spam Detector 垃圾电话检测
freeCodeCamp Python 每日编程挑战 48用正则与数字规则实现 Spam Detector 垃圾电话检测【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp导读本篇技术指南以 freeCodeCamp 仓库中curriculum/challenges/english/blocks/daily-coding-challenges-python/68b7687dded630607aceccb1.md这份 Python 每日编程挑战文档为核心完整讲解 Challenge 48: Spam Detector 的题目要求、四条垃圾号码判定规则、8 个官方测试用例并逐行拆解仓库自带的参考解法——一个融合re正则解析、字符串子串匹配与连续重复检测的综合实现。读完本文你将掌握如何在 Python 中解析形如A (BBB) CCC-DDDD的国际电话号码、如何用正则分组提取各部分、如何把数字规则判定与字符规律检测组合成一个完整的布尔判断函数并能通过本地 unittest 用例验证实现。一、挑战背景freeCodeCamp 每日编程挑战Python 版Spam Detector 是 freeCodeCamp 课程体系中daily-coding-challenges-python板块的第 48 道题。该板块共包含 365 道 Python 每日编程挑战存放在 curriculum/challenges/english/blocks/daily-coding-challenges-python 目录下按日期每天发布一道。从板块元数据 curriculum/structure/blocks/daily-coding-challenges-python.json 可以看到它的定位isUpcomingChange: true属于即将上线的实验性内容helpCategory: Python归类为 Python 帮助类别usesMultifileEditor: true使用多文件编辑器。每个 Python 挑战都与 JavaScript 板块daily-coding-challenges-javascript中同 id 的挑战一一对应本题的 JS 孪生版见 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/68b7687dded630607aceccb1.md两者题目与测试完全对齐。这一对应关系由课程测试 curriculum/src/test/daily-challenges.test.js 强制校验测试会断言 JS 与 Python 挑战的数量相等、id 一一对应、标题与描述一致、测试用例数量一致因此两套题目的判定规则与用例完全相同。二、题目描述与输入格式解析2.1 输入格式函数接收一个字符串格式的电话号码A (BBB) CCC-DDDD其中每个字母代表一位数字具体约定如下部分含义长度约束A国家代码country code任意位数BBB区号area code恒为 3 位CCC本地号码前三段恒为 3 位DDDD本地号码后四段恒为 4 位典型输入示例0 (555) 135-0192、091 (555) 309-1922。2.2 垃圾号码Spam判定规则is_spam(number)应返回布尔值当满足以下任意一条时即为垃圾号码返回True国家代码异常国家代码位数大于 2或者不以0开头区号越界区号大于900或小于200本地号码数字和子串本地号码前三位CCC的各位数字之和作为字符串出现在后四位DDDD中连续重复数字忽略格式字符、(、)、空格、-后号码中存在同一数字连续出现 4 次或更多次。四条规则是或OR关系——只要命中其一即判定为垃圾号码全部不命中才返回False。三、官方测试用例Hints逐条解读挑战文档在--hints--段给出了 8 个测试用例全部通过 Python 内置unittest的assertIs进行断言。下面逐条分析每个用例命中的规则输入期望结果命中规则分析0 (200) 234-0182False国家码0以 0 开头且 1 位区号200在 [200, 900] 内CCC234数字和9不在0182中无 4 连重复。完全合规091 (555) 309-1922True国家码091为 3 位大于 2 位 → 规则 11 (555) 435-4792True国家码1不以0开头 → 规则 10 (955) 234-4364True区号955大于900→ 规则 20 (155) 131-6943True区号155小于200→ 规则 20 (555) 135-0192TrueCCC135数字和9字符串9出现在DDDD0192中 → 规则 30 (555) 564-1987TrueCCC564数字和1515出现在DDDD1987中 → 规则 300 (555) 234-0182False国家码00为 2 位且以 0 开头其余均合规。注意虽然数字串中有两个0相邻但未达到4 连重复故为False从测试设计可以看出命题者有意覆盖了全部四条规则的命中与不命中边界情形特别是第 8 个用例用于排除2 连 0误判为 4 连重复的情况。四、参考解法逐行拆解挑战文档--solutions--段给出了官方参考实现这是理解本题的最佳范本import re def is_spam(number): digits re.sub(r\D, , number) match re.match(r^\(\d)\s\((\d{3})\)\s(\d{3})-(\d{4})$, number) country_code, area_code, ccc, dddd match.groups() if len(country_code) 2 or not country_code.startswith(0): return True area_num int(area_code) if area_num 900 or area_num 200: return True sum_ccc sum(int(d) for d in ccc) if str(sum_ccc) in dddd: return True if re.search(r(\d)\1\1\1, digits): return True return False4.1 提取纯数字串re.sub(r\D, , number)digits re.sub(r\D, , number)\D匹配任意非数字字符re.sub将其全部替换为空串得到纯数字串digits。这一步服务于规则 4连续重复检测确保检测在忽略格式字符的前提下进行。例如0 (555) 135-0192→05551350192。此外它还起到了隐式的防御作用即使输入中出现意外字符也不会干扰后续重复检测。4.2 用正则分组解析号码re.match 捕获组match re.match(r^\(\d)\s\((\d{3})\)\s(\d{3})-(\d{4})$, number) country_code, area_code, ccc, dddd match.groups()这是整个解法的核心正则逐段解读正则片段含义^\匹配开头的号需转义(\d)捕获组 1国家代码\d表示一位或多位数字\s匹配一个空格\((\d{3})\)捕获组 2括号包裹的三位区号\s空格(\d{3})捕获组 3本地号码前三位CCC-连字符(\d{4})捕获组 4本地号码后四位DDDD$锚定字符串结尾match.groups()依次返回四个捕获组元组解包赋值给country_code, area_code, ccc, dddd四个变量。注意\d是贪婪匹配但后续的\s\((\d{3})\)提供了边界锚点因此国家代码能正确截断。值得对比的是 JS 孪生解法见 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/68b7687dded630607aceccb1.md使用了几乎相同的正则/^\(\d)\s\((\d{3})\)\s(\d{3})-(\d{4})$/只是(\d)\1\1\1的重复检测直接作用于拼接后的allDigits字符串而非re.sub的结果——两种语言的正则思路完全同构可作为跨语言对照学习材料。4.3 规则 1国家代码校验if len(country_code) 2 or not country_code.startswith(0): return Truelen(country_code) 2国家代码超过 2 位not country_code.startswith(0)不以0开头。二者满足其一即返回True。这里用的是字符串方法而非数值比较因为国家代码以0开头的语义用startswith表达最直接也避免前导零在int()转换中被丢弃的问题。4.4 规则 2区号数值区间校验area_num int(area_code) if area_num 900 or area_num 200: return True将三位区号字符串转为整数后与数值边界比较大于900不含或小于200不含即为垃圾号码。合法区间为闭区间[200, 900]。4.5 规则 3数字和子串匹配sum_ccc sum(int(d) for d in ccc) if str(sum_ccc) in dddd: return Truesum(int(d) for d in ccc)生成器表达式把ccc的每个字符转成整数并求和str(sum_ccc) in dddd把数字和转回字符串判断其是否为dddd的子串in运算符做子串包含判断。子串判断是字符串包含而非数值相等因此9 in 0192与15 in 1987都能正确命中。4.6 规则 4连续重复数字检测if re.search(r(\d)\1\1\1, digits): return True(\d)是一个捕获组匹配任意一位数字并记住它\1是对该捕获组的反向引用backreference要求当前位置的字符与捕获组记录的字符相同。连写三个\1即要求同一数字连续出现 4 次。re.search在digits已剔除格式字符的纯数字串中查找任意位置是否匹配。需要特别说明\1反向引用在 Python 正则中作用于捕获组内容本身单字符匹配结果因此该模式等价于同一字符连续出现 4 次而不会出现前一次匹配的 4 位重复数字在后一次被整体复用的歧义这正是 JS 版与 Python 版采用同一写法的原因。4.7 返回逻辑return False四条规则全部未命中时返回False表示该号码不是垃圾号码。五、从挑战到实战每日挑战在仓库中的完整链路这道挑战题不仅是一份独立的练习文档在仓库中还串起了一条完整的数据链路课程源挑战 Markdown 存放在 curriculum/challenges/english/blocks/daily-coding-challenges-python/按challengeType: 29Python 挑战类型与 JS 版challengeType: 28配对课程校验curriculum/src/test/daily-challenges.test.js 使用 vitest 从dev-playgroundsuperBlock 拉取挑战断言 JS/Python 两套挑战的数量、id、标题、描述与测试用例数完全对齐入库播种tools/daily-challenges/seed-daily-challenges.ts 从 GraphQL 拉取两套挑战按 1:1 配对后写入 MongoDB 的DailyCodingChallenges集合每个挑战按起始日期2025-08-11起逐日顺延一天365 道题正好覆盖一整年API 暴露api/src/daily-coding-challenge/routes/daily-coding-challenge.ts 提供GET /daily-coding-challenge/date/:date等路由前端通过日期参数获取当天的挑战数据含题目、测试与种子代码并用today、month、all、newest等路由支持日历视图。也就是说当你在 freeCodeCamp 平台上打开今天的每日挑战时看到的正是由这道is_spam挑战这类 Markdown 经播种、入库、API 查询后渲染出来的内容——理解这一链路有助于把做题和理解平台工作机制结合起来。六、本地运行与验证挑战的--hints--段使用unittest驱动测试你可以把以下脚本保存为本地文件将解法代码填入is_spam然后运行python3 spam_detector_test.py验证全部 8 个用例from unittest import TestCase def is_spam(number): import re digits re.sub(r\D, , number) match re.match(r^\(\d)\s\((\d{3})\)\s(\d{3})-(\d{4})$, number) country_code, area_code, ccc, dddd match.groups() if len(country_code) 2 or not country_code.startswith(0): return True area_num int(area_code) if area_num 900 or area_num 200: return True sum_ccc sum(int(d) for d in ccc) if str(sum_ccc) in dddd: return True if re.search(r(\d)\1\1\1, digits): return True return False TestCase().assertIs(is_spam(0 (200) 234-0182), False) TestCase().assertIs(is_spam(091 (555) 309-1922), True) TestCase().assertIs(is_spam(1 (555) 435-4792), True) TestCase().assertIs(is_spam(0 (955) 234-4364), True) TestCase().assertIs(is_spam(0 (155) 131-6943), True) TestCase().assertIs(is_spam(0 (555) 135-0192), True) TestCase().assertIs(is_spam(0 (555) 564-1987), True) TestCase().assertIs(is_spam(00 (555) 234-0182), False) print(All 8 test cases passed!)若所有断言通过控制台会输出All 8 test cases passed!说明你的实现与官方参考解法在 8 个用例上行为完全一致。七、扩展思考与易错点顺序无关的 OR 判定四条规则彼此独立且为或关系实现时可以按任意顺序依次短路返回True。官方解法把开销最小的字符串长度/前缀判断放在最前把需要正则二次扫描的重复检测放在最后这种先廉价后昂贵的排列是值得借鉴的工程习惯。前导零陷阱规则 1 判断国家代码以 0 开头必须用字符串startswith(0)。若先用int()转数值再比较00会变成0导致00与0无法区分——官方用例第 8 条00 (555) 234-0182 → False正是为这类实现设的坑。子串而非子序列规则 3 用的是字符串in判断子串包含不是数字和是否等于 DDDD或数字和各位是否按序出现的数值逻辑改写时切勿混淆。反向引用的边界(\d)\1\1\1要求同一个数字连续 4 次而不是任意 4 位重复模式。例如1212不满足0000才满足。正则锚点re.match从字符串开头匹配配合^...$锚点可确保整个输入严格符合A (BBB) CCC-DDDD格式若格式不符会得到None因此官方解法隐含了输入格式合法的前置假设。八、小结Spam Detector 是一道规则拆解 正则应用的综合训练题通过re.sub清洗格式、re.match分组解析、字符串方法完成数值规则、(\d)\1\1\1反向引用完成规律检测最终把四条业务规则翻译成约 15 行 Python 代码。它与 JS 孪生版共享同一套判定逻辑又由 curriculum/src/test/daily-challenges.test.js 保证双语言对齐是理解 freeCodeCamp 每日挑战体系从 Markdown 课程源到播种、API、前端渲染全链路的一个绝佳切入点。继续挑战下一个题目Challenge 49: CSV Header Parser或回溯本板块的完整挑战列表见 curriculum/structure/blocks/daily-coding-challenges-python.json可以进一步巩固 Python 正则与字符串处理能力。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

ECC 规则体系下的 Swift 编码风格指南:格式化、不可变性、错误处理与并发实践

ECC 规则体系下的 Swift 编码风格指南:格式化、不可变性、错误处理与并发实践

ECC 规则体系下的 Swift 编码风格指南:格式化、不可变性、错误处理与并发实践 【免费下载链接】ECC The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode,…

📅 2026/9/10 16:56:20
智慧供热物联网远程监控系统方案解析

智慧供热物联网远程监控系统方案解析

一、方案背景供热行业是关乎民生的重要基础产业,其生产运行涉及热源制备、管网输送、换热站调控、用户终端服务等多个复杂环节。传统供热企业普遍面临诸多挑战:各换热站、锅炉房内的PLC、热量表、压力计、流量计等设备独立运行,运行数据分散留…

📅 2026/9/10 16:51:19
LeetCode 25. Reverse Nodes in k-Group 题解:Go 递归实现 K 个一组反转链表

LeetCode 25. Reverse Nodes in k-Group 题解:Go 递归实现 K 个一组反转链表

LeetCode 25. Reverse Nodes in k-Group 题解:Go 递归实现 K 个一组反转链表 【免费下载链接】LeetCode-Go ✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解 项目地址: https://gitcode.com/GitHub_Trending/le/LeetCod…

📅 2026/9/10 16:51:19
MORE NEWS

更多资讯

📰

企业电脑监控软件免费试用选型指南:避坑实测路线

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Bootstrap 4栅格系统深度解析:Flexbox引擎如何重塑布局

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

9款AI写作工具助力学术论文高效产出

1. 写作工具革命:从焦虑到高效输出的跨越每次看到论文截止日期临近而文档还是一片空白时,那种胃部紧缩的感觉我太熟悉了。作为从写作小白一路摸爬滚打过来的学术民工,我深知那种面对空白文档大脑也一片空白的绝望。直到三年前偶然接触到AI写作…

📰

共享储能电站优化调度模型与工程实践

1. 共享储能电站的经济调度挑战与行业背景2023年夏季某省电网的负荷曲线显示,光伏电站午间发电量超出本地消纳能力23%,而晚高峰时段需启动燃气调峰机组填补电力缺口。这种典型的"鸭子曲线"现象,正是共享储能电站最能发挥价值的场景…

📰

乙巳年腊月二十“向心行”:年末复盘与新年规划指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

使用 impeccable adapt 完成 iOS / Android 原生界面适配:从重排版到重塑体验的完整指南

使用 impeccable adapt 完成 iOS / Android 原生界面适配:从重排版到重塑体验的完整指南 【免费下载链接】impeccable The design language that makes your AI harness better at design. 项目地址: https://gitcode.com/GitHub_Trending/im/impeccable 本文…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬