正则表达式校验全攻略:避开贪婪匹配与跨语言陷阱
发布时间:2026/9/28 8:09:57 锦皓数字建站

做开发这些年正则表达式是我见过“看起来简单用起来翻车率最高”的工具之一。尤其是各种校验场景手机号、邮箱、身份证、金额、文件编号……几乎每个项目都要写一遍但很多人写出来要么匹配不全、要么误杀合法数据甚至因为一个贪婪匹配把线上接口拖慢几十倍。正则校验这件事表面上是背几个 pattern实际上牵扯到匹配语义、字符边界、语言差异、性能陷阱还有“正则到底该干到哪一步”的边界判断。这篇文章不打算只给一份“20 个常用正则”的清单那种东西网上到处都是背下来也解决不了实际问题。我想做的是把校验场景里真正会用到的正则思路、常见误区、跨语言差异以及那些“看起来该用正则、实际不该用正则”的地方一次性讲透。内容基于我在多个项目中整理过的校验规则汇总覆盖格式校验、提取场景、自定义校验框架、CRC/MD5 这类“非正则校验”的区分以及一份可以直接抄作业的避坑清单。适合写前端表单、后端接口参数校验、数据处理脚本的开发者阅读无论你用 JavaScript、Python、Java 还是 C#核心思路都一样语言差异我会专门列一节说明。1. 写在前面正则校验之前先把 3 个底层概念钉死正则的坑绝大多数不是“不会写”而是“没理解正则到底怎么工作”。我见过太多人拿着一个 pattern 调了半天最后发现问题是把“包含”当成了“匹配”或者漏了锚点导致一串非法数据全放过去了。这三个概念搞不清楚后面的汇总表只能让你踩更多的坑。1.1 校验的本质是“全匹配”不是“包含关系”正则引擎扫描字符串时默认行为是在任意位置找到一个符合条件的子串就算成功。Python 的re.search()、JavaScript 的test()、Java 的find()都是这个语义。但“校验”这个动作要求的是“整个字符串必须符合规则”也就是全匹配。这两者的区别就是门禁闸机和“人群里找一个人”的区别闸机要求你整个人符合证件信息才放行而找人只要在人群里发现一个疑似目标就触发报警。举个例子校验手机号如果用re.search(r1[3-9]\d{9}, text)输入abc13812345678xyz也能通过因为引擎在字符串中间找到了符合条件的片段。正确做法是补上锚点用^和$包围或者直接用 Python 的re.fullmatch()、JavaScript 里给 pattern 加^$包围。这也是我在代码评审里看到频率最高的问题不是正则写错了是压根没做“全字符串匹配”的语义约束。注意^和$在 JavaScript 中默认匹配字符串开头和结尾但在多行模式下m标志会让它们匹配每行的开头和结尾。做严格校验时如果开了多行模式务必用\A和\zPython 风格或者\A和\Z避免一行的边界变成整个字符串的边界。1.2 锚点就是校验的“边界感”锚点不止^和$这两个。\b表示单词边界(?...)和(?...)是零宽断言它们不消费字符只判断位置。校验场景里最容易被忽略的是$的行为差异在 JavaScript 里$默认匹配字符串末尾但会忽略末尾的一个换行符而在 Java 的matches()方法里它要求整个字符串完全匹配不需要额外加$。这些细节看起来琐碎但在“身份证号末尾是 X”这类数据上会直接导致线上事故。还有一类边界问题是“包含数字”和“全由数字组成”的区别。\d匹配一个数字字符^\d$才匹配“全由数字组成”。业务上校验验证码、订单号、银行卡号时漏掉或锚点的现象非常普遍我在后面每一个示例里都会刻意强调长记性比背 pattern 重要得多。1.3 贪婪、懒惰和回溯性能翻车的根源默认情况下量词是贪婪的.*会一路匹配到字符串末尾再往回退这个过程叫回溯。回溯本身不是问题但嵌套量词会让回溯呈指数增长形成灾难性回溯CPU 直接被打满。最经典的例子是^(a)$输入一串a再跟一个b引擎会在嵌套分组里反复尝试耗时随字符串长度指数上升。这类问题在用户输入的“正则搜索”功能里尤其致命因为攻击者可以构造特殊字符串让服务崩溃。做校验时优先用字符类限定内容比如[A-Z0-9]{6,12}而不是.用懒惰量词.*?只在确实需要时才用。如果可能加一个长度上限比如{1,64}这既能保证业务合理性也能顺手兜住性能风险。后面第 6 章的速查表里我会专门列出这类问题以及复现方式。2. 高频业务校验正则大全与逐条拆解这是全文的核心资料区。我会按业务场景分类每个正则都给出“能用但不推荐”和“推荐写法”的对比并解释为什么推荐。这里的 pattern 我尽量做到跨语言基本通用语言特定的差异留到第 4 章。2.1 手机号、电话与联系方式校验国内手机号校验很多人还在用^1[3-9]\d{9}$这个 pattern 本身没错但要注意它已经是一个“放水版”它不判断号段是否真实存在只判断首位和长度。业务上我建议就用这个宽松版千万不要自己去维护一份“号段白名单”正则因为号段每个月都在新增你写死了过两年就会误杀新号段用户。真要限制就控制在 11 位数字、1开头、第二位3-9这个规则兼顾了准确性和维护成本。固定电话的校验稍微麻烦一点区号 3-4 位电话号码 7-8 位分机号可选项。推荐写法是^0\d{2,3}-?\d{7,8}(-\d{1,6})?$。这里有个细节-?表示短横线可有可无但如果你希望“要么就没有要么就带短横线”那这个写法是正确的如果你希望“有短横线必须成对出现”就得用更复杂的条件写法但实际业务里很少这么苛刻。场景推荐正则说明手机号^1[3-9]\d{9}$宽松校验避免号段维护固定电话^0\d{2,3}-?\d{7,8}(-\d{1,6})?$支持分机号可选400 电话^400(-\d{3,4}){2}$实际使用较少按需添加2.2 邮箱、URL 与 IP 地址校验邮箱校验是“正则滥用”的重灾区。RDC 5322 标准的邮箱格式极其复杂完整的正则几百个字符都不够而业务上我们根本不需要那么严格。推荐做法是只做“基础形状校验”^[\w.-][\w-]\.[\w.-]$。这个 pattern 允许点号、加号、连字符足以拦截 99% 的明显错误输入。真正要“确认邮箱存在”唯一的办法是发验证邮件正则再牛也做不到这一步。URL 校验同样不要追求完美。推荐写法是^https?://[\w-](\.[\w-])(:\d{1,5})?(/[\w./?%-]*)?$。注意(:\d{1,5})?里的端口号最多 5 位数字范围 0-65535但正则没法判断“65536”这种越界值所以如果端口影响业务必须在代码里额外做数字范围判断。这是我反复强调的一句话正则管格式边界值管范围两者是组合关系不是替代关系。IP 地址的校验在网上能找到各种版本但最常见的坑是把 IPv4 写成^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$这样999.1.1.1也能通过。严格写法要对每一段做范围约束^((25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$。这个 pattern 看着长其实逻辑很简单250-255、200-249、100-199、0-99 四种情况分别写出来然后.重复三次。我更推荐的做法是用代码拆分判断因为可读性远好于这一长串正则见第 3 章的“正则代码组合拳”。2.3 身份证号、日期与金额校验身份证号校验是“正则算法”的典型。正则部分很简单^\d{17}[\dXx]$先保证 18 位且最后一位是数字或 X。但真正严格的身份证校验必须校验校验位前 17 位做一个加权求和模 11 的结果映射到对应校验码。正则做不到这件事只能在代码里写算法。很多系统只用正则应付导致随便编一个符合位数规则的号码就能通过这在实际业务里是重大漏洞。日期校验的难点是“每个月天数不同还要考虑闰年”。纯粹用正则写出“完整正确”的日期校验结果就是一个超长的分支组合维护成本极高。我的建议是分两层先用简单正则^\d{4}-\d{2}-\d{2}$拦截格式错误再交给代码判断日期的真实合法性。比如用 Python 的datetime.strptime()或 JavaScript 的Date解析解析失败就拒绝。这比写一个“能判断闰年 2 月 29 日”的正则靠谱得多。金额校验分两种情况一种是不允许负数和千分位只要^\d(\.\d{1,2})?$用于表单输入。另一种是展示格式校验要求带千分位逗号比如^\d{1,3}(,\d{3})*(\.\d{1,2})?$这个 pattern 要求整数部分每三位一个逗号。注意后者只适合“展示格式”校验不适合“用户输入”校验因为用户不会主动输入逗号。你一旦把这两类校验混用就会出现“用户老老实实填了 1000却被系统判定格式错误”的闹剧。3. 正则解决不了的事组合思路与自定义校验框架正则不是万能的。它擅长的是“格式形状检查”和“按模式提取”但在“数值范围判断”、“逻辑关系验证”、“跨字段校验”这些场景上强行用正则只会让代码变成无人能维护的天书。这一章我分享几个“正则代码”的实战组合这也是我认为一个合格开发者跟新手最大的分水岭。3.1 案例实战从文本中提取数字及 # 号后的字符串很多业务场景里需要从一段描述文本中提取关键信息。比如设备编号、订单号或者用户备注里以#包围的标签。给定输入订单#A138#已发货数量 45 件我需要同时提取出45和A138。先说数字提取。最直接的做法是\d但这里有个隐藏问题如果#A138#中的138也被当成数字提取就会拿到不想要的值。正确做法是先提取#...#包裹的内容再从外面找数字。用 Python 的话我一般分两步import re text 订单#A138#已发货数量 45 件 # 第一步提取 # 号包裹的标签 tag_match re.search(r#([^#])#, text) tag tag_match.group(1) if tag_match else None # 第二步在剥离标签后的文本中提取数字避免误提取 cleaned re.sub(r#([^#])#, , text) numbers re.findall(r\d, cleaned)这个思路有好几层讲究。[^#]表示“非 # 的字符至少一个”比.更安全因为它不会跨过另一个#先剥离标签再提取数字避免了标签内数字被误收re.findall()返回所有匹配项方便后续处理。JavaScript 版本也类似const text 订单#A138#已发货数量 45 件; const tagMatch text.match(/#([^#])#/); const tag tagMatch ? tagMatch[1] : null; const cleaned text.replace(/#[^#]#/g, ); const numbers cleaned.match(/\d/g) || [];这里的关键不是 pattern 本身而是“顺序”先做高优先级的结构化提取再做低优先级的通用提取避免结构化信息污染通用匹配结果。这个思路可以扩展到很多场景比如从日志里先提取 JSON 块、再提取时间戳先提取keyvalue对、再提取裸数字。3.2 数字范围与业务规则的边界不要硬写正则校验“1-100 的整数”你当然可以写^([1-9]|[1-9]\d|100)$看起来也不是很复杂。但如果是“1-255”呢^([1-9]|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$已经有点绕了。如果范围是“1-99999”呢再往上加分支正则的可读性就会变成灾难。我个人的铁律是数值范围判断全部交给代码。理由有三点第一正则的数值范围分支本质上是“手写十进制逻辑”容易漏边界第二正则引擎没有“数值比较”的概念写的再花哨也只是字符串匹配的等价替换第三代码里的if (value 1 value 100)一目了然reviewer 不需要花 10 分钟去验证1\d{2}到底包含了 100 还是 200。有一个例外IP 地址每一段的 0-255 校验可以考虑用正则因为它已经是行业内的“固定公式”直接抄标准写法比写四段if更紧凑。但即便如此我也会在正则外面加一个代码层的注释说明这段写法等价于0 value 255的范围判断。3.3 从“单个校验”到“表单校验规则”一个通用校验器设计实际项目里单个字段校验只是第一步一整个表单可能有十几个字段每个字段要校验类型、长度、非空、格式还要支持自定义校验。我设计过一个轻量级的校验配置方案思路是“用规则对象描述表单而不是每个字段手写 if-else”。核心结构很简单每个字段对应一个校验规则数组每个规则包含validator、message、required等字段。例如const rules { username: [ { required: true, message: 用户名不能为空 }, { pattern: /^[a-zA-Z0-9_]{4,16}$/, message: 用户名需为4-16位字母数字下划线 } ], email: [ { required: true, message: 邮箱不能为空 }, { pattern: /^[\w.-][\w-]\.[\w.-]$/, message: 邮箱格式不正确 }, { validator: async (value) !(await isEmailTaken(value)), message: 邮箱已被注册 } ], age: [ { validator: (value) value 18 value 60, message: 年龄需在18-60之间 } ] };这个方案的可扩展性很好pattern负责格式校验validator负责代码逻辑校验async函数负责远程校验统一由调度器遍历 rules 收集错误信息。重点在于设计原则正则在规则里承担“格式层”职责代码函数承担“逻辑层”职责两者各司其职。后面第 6 章我会给出一个更完整的参考实现这里先记住这个分层思想。4. 跨语言正则校验的差异与移植避坑正则的语法标准叫 PCREPerl Compatible Regular Expressions但不同语言引擎实现却不完全相同。一个 pattern 在 JavaScript 里好用放到 Python 或者 Java 里可能行为不同甚至直接报错。这一章我梳理做校验时最容易踩的跨语言差异以及我在迁移正则时必查的几个点。4.1 JavaScript、Python、Java、C#、PHP 的语法差异对比先说转义。这是新手最容易懵的地方。在 Python、Java、C# 等语言的字符串字面量里\d写进字符串字面量时反斜杠本身是转义符所以你可能需要写成\\d或者使用 Python 的 raw stringr\d和 C# 的逐字字符串\d。在 JavaScript 里正则直接写在/.../字面量中\d不需要额外转义但如果用new RegExp(\\d)构造就又需要双反斜杠了。这个差异没有捷径只能形成“肌肉记忆”写正则前先确认当前语言里反斜杠的转义层级。再看匹配语义。Java 的String.matches()要求“整个字符串完全匹配”等价于在前后自动添加了锚点而 Python 的re.search()是“搜索子串”JavaScript 的test()也是“搜索子串”。这意味着同一个 pattern 在 Java 里做校验没问题搬到 Python 里就必须自己加^$或者改用re.fullmatch()。我做跨语言校验时一定会写一个“适配器”统一封装fullMatch(pattern, text)函数底层分别调用对应语言的 fullmatch 或 matches 方法这样上层业务逻辑就免于受到语言差异的干扰。最后是高级特性的支持差异。JavaScript 早期不支持(?...)后行断言虽然现代 Node.js 已经支持但如果你要兼容老浏览器或老运行环境只能改写为不带后行断言的版本。Java 的预编译Pattern.compile()需要处理\的字符串转义。C# 默认不开RegexOptions.ECMAScript时不少字符类的行为也和 PCRE 不完全一致。PHP 的preg_match依赖 PCRE相对标准但它要求 pattern 带/分隔符或指定其他分隔符。语言全匹配方法字符串中的反斜杠写法后行断言支持JavaScript加^$或fullmatch()不存在自带方法字面量/.../内不需双写现代版本支持Pythonre.fullmatch()r\d支持JavaString.matches()\\d支持C#Regex.IsMatch\A\z或整串设计\d支持PHPpreg_match(/.../, $s)/\\d/支持4.2 前后端校验一致性问题与“中间层校验”前后端校验不一致是表单类项目的一个经典事故源。前端只校验格式后端只判空和长度两边规则对不上结果就是前端提示“手机号格式错误”后端却能接收13812345678后面跟乱码的数据。更隐蔽的情况是前端改了一条规则后端没同步改导致某些数据“前端进不来后端能进来”。我的建议是把校验规则定义成一份“共享配置”用 JSON Schema、ZodJavaScript、PydanticPython这类工具后端和前端共用同一套 schema。比如用 JSON Schema 定义字段格式、类型、范围前后端分别用各自的库去解析同一份 JSON。这样规则只维护一份格式校验、类型校验、范围校验都能统一。正则在这套体系里作为pattern关键字出现仍然负责格式层。也不要忘了服务端作为“最后一道防线”即使前端做了完整校验后端也必须再跑一遍同样的规则——不要相信任何来自客户端的“已通过校验”标记这是系统安全的基本底线。4.3 常见语言的正则函数“使用习惯”推荐JavaScript优先用test()而不是match()因为test()只返回布尔值专门用于校验match()返回数组或 null用于提取内容。Python优先用re.fullmatch()做全串校验re.match()只匹配开头经常产生“字符串开头符合但后面乱写也能过”的错觉。Java用Pattern.compile()预编译不要在循环里反复matches()否则性能会打折扣预编译后的 Pattern 是线程安全的可以复用。C#建议用Regex.IsMatch(input, pattern, RegexOptions.None)同时在正则前用\A和\z而不是^和$避免Multiline选项带来的边界混乱。5. 校验不只有正则CRC、校验和与文件哈希的正确姿势我特意留一章讲“非正则校验”因为很多搜索正则的人实际上要找的并不一定是正则。比如“CRC16 校验”、“CRC32 校验”、“MD5 校验工具”、“文件 hash 校验”这些跟正则没有任何关系它们的目的是“数据完整性校验”而不是“格式校验”。把这两类概念混为一谈是很多项目技术方案走弯路的原因。5.1 格式校验与完整性校验的区别正则校验回答的是“这个值长得对不对”。CRC 校验、校验和、MD5、SHA 哈希回答的是“这段数据有没有被篡改、有没有传错”。前者是“形状”问题后者是“一致性”问题。典型的例子下载文件时网页会给出 MD5 或 SHA256 值你下载完在本地计算一遍如果一致说明文件在网络传输中没有损坏。这个场景里没有任何一个字符需要“符合某种格式”它需要的是“两个哈希值相同”。这里有一个实际项目的混淆案例有人想校验用户输入的“校验码”是否正确搜到“CRC 校验”的帖子以为用正则就能算出来折腾半天没结果。实际上校验码通常是由服务器按某种算法生成的可能是 CRC、MD5也可能是自定义加盐哈希客户端能做的只有“把输入提交给服务器比对”或者“本地用同样的算法重新计算后比对”正则完全参与不了。5.2 CRC16 与 CRC32 校验的理解与实现CRC循环冗余校验是一种基于多项式除法的校验算法被广泛用于通信协议、存储介质校验。CRC16 生成 16 位校验值CRC32 生成 32 位校验值具体多项式参数有很多变种如 CRC-16/MODBUS、CRC-32/ISO-HDLC选错参数会导致计算结果不一致。实际做嵌入式或者协议对接时一定要先确认双方约定的是哪一种 CRC 变种通常协议文档里会明确标注。在软件层面CRC 的实现一般不需要自己写位运算直接用现成库即可。Python 中可以用binascii.crc32()或zlib.crc32()import binascii import zlib data bhello world print(hex(binascii.crc32(data))) # 0x4b9d5b81 print(hex(zlib.crc32(data))) # 0x4b9d5b81结果一致C# 里可以用System.IO.Hashing.Crc32.NET 6或第三方库JavaScript 里可以找crc-32这个 npm 包。重点再次强调CRC 的值是算法算出来的不是靠正则匹配出来的。如果你只想验证一个字符串自身的完整性防止传输出错CRC 是轻量选择如果需要防恶意篡改CRC 不顶用必须用带密钥的 HMAC 或 SHA-256。5.3 文件哈希校验全流程MD5 与 SHA 实操文件完整性校验的流程非常固定先计算原始文件的哈希值再计算你手上这份文件的哈希值两个值一致则视为同一文件。系统自带的工具就够用我平时用的是 PowerShell 和 Python 两种方式备选。PowerShell 计算 SHA256Get-FileHash -Path .\backup.zip -Algorithm SHA256Python 计算 MD5 和 SHA256import hashlib def file_hash(path, algorithmsha256): h hashlib.new(algorithm) with open(path, rb) as f: for chunk in iter(lambda: f.read(65536), b): h.update(chunk) return h.hexdigest() print(file_hash(backup.zip, md5)) print(file_hash(backup.zip, sha256))这里有个细节大文件绝不能一次性read()进内存计算哈希几十 GB 的文件会直接占满内存。分块读取是标准做法65536 字节一块在性能和内存占用上比较均衡。MD5 因为碰撞攻击和彩虹表问题已经不适合用于安全场景更推荐 SHA-256 甚至 SHA-512。但校验场景如果只是为了检测“偶发的传输错误”MD5 仍然可以用因为它计算快、输出短前提是你清楚它的安全边界。顺带一提热搜词里的“校验和”也是同样的思路通过把所有数据按某种规则相加得到一个数值接收方重新计算并对比。IP 头校验和、UDP/TCP 校验和都是这种机制。它们同样跟正则无关属于“完整性校验”家族。所以当你发现自己在搜“正则校验”却找到 CRC 时先停下来想想我到底要校验“格式”还是“完整性”想清楚这个方案自然就对了。6. 高频问题速查表与完整校验函数参考最后这部分是我从实际项目里总结的高频翻车场景和避坑记录。有些是我自己踩的有些是帮同事 review 代码时发现的每条都对应过线上或准线上的问题。做技术分享我不喜欢讲空话直接上问题和解决方案。6.1 十个高频翻车现场与解决办法问题根因解决办法手机号校验能匹配abc13812345678xyz用了search()/test()没有做全串匹配加^$或改用fullmatch()\d在 Java 字符串里报错字符串字面量把反斜杠吞了写\\d或Pattern.compile时注意转义URL 校验把http://写成了http//中文输入法下的全角冒号写正则前先检查字符是否为 ASCII 半角邮箱正则太严格usertagexample.com被拒用了过时的邮箱正则模板使用^[\w.-][\w-]\.[\w.-]$日期2024-02-30通过了校验正则只查格式不查日历正则后补代码层日期有效性判断贪婪匹配导致性能下降用.*匹配长文本用字符类限定[^]*、[A-Z0-9]*等前后端校验规则不一致脏数据入库两套规则手写没同步用共享 schema 或统一校验层IP 校验999.1.1.1通过只写了\d{1,3}用范围版 pattern 或代码分段判断提取数字时把标签内数字也拿走了先用\d全局提取先剥离结构化内容再提取通用内容在 JS 里用后行断言老浏览器报错(?...)部分环境不支持改写为捕获组或代码逻辑判断这张表的每一条我在前面的章节里都展开过。但再看一遍还有个好处当你面对“校验失败”的报错时可以像查字典一样按根因去定位。排查顺序一般是先看匹配语义全匹配还是子串再看转义再看锚点再看高级特性最后看性能。按这个顺序走一般不用半小时就能定位到问题。6.2 一个可复用的校验函数设计参考把前面所有思路合成一个“最终成品”之前先说清楚适用场景这是一个浏览器端的纯前端校验器用 JavaScript 编写同时给出 Python 侧等价实现思路。它的特点是支持必填、格式、逻辑三个纬度并且能一次性返回所有字段的错误信息而不是碰到第一个错误就停止。下面这个版本我刻意写得比较简单清楚方便你按自己的项目去裁剪function validate(rules, data) { const errors {}; for (const [field, checks] of Object.entries(rules)) { const value data[field]; for (const check of checks) { if (check.required (value undefined || value null || value )) { errors[field] check.message || ${field} 不能为空; break; } if (check.pattern value ! undefined) { const regex typeof check.pattern string ? new RegExp(check.pattern) : check.pattern; // 注意这里用 !regex.test(value) 判断格式失败 if (!regex.test(value)) { errors[field] check.message || ${field} 格式不正确; break; } } if (check.validator value ! undefined) { const pass check.validator(value); if (pass false || (pass pass.then)) { // 此处略去 Promise 分支的具体处理动态表单校验常会用到 } if (pass false) { errors[field] check.message || ${field} 校验失败; break; } } } } return errors; }这段代码的三个检查纬度正好对应前面反复说的三个层required对应“空值检查”pattern对应“格式检查”validator对应“逻辑检查”。实际项目中还会加type检查string、number、array以及异步 validator 的 Promise 处理但骨架就是这个样子。对应到 Python 侧用 Pydantic 的Field(pattern...)、validator装饰器可以达到同样效果而且类型检查更强。6.3 项目落地时的三条个人建议最后讲几条我在多个项目里反复验证过的经验不算什么高深理论但每一条都在关键时刻帮我避过坑。第一正则尽量写“宽松且合理”不要追求“绝对准确”。手机号验证“1 开头、第二位 3-9、共 11 位”就足够了不要去维护号段表邮箱验证“有 、有域名、点号结构合理”就足够不要试图百分百还原 RFC 标准。宽松校验减少误杀业务规则里真正要紧的是“不能空、不能明显乱填”剩下的靠验证码、验证邮件、短信验证码这些高成本手段兜底。第二任何用户输入的校验永远在后端再做一遍。前端校验的初衷是“提升用户体验”不是“安全边界”。一个懂技术的人可以轻易绕开前端直接向后端接口提交数据所以后端必须把格式校验、长度校验、范围校验、业务校验都跑一遍。我见过太多项目“前端校验明明做了数据库里全是脏数据”一查后端只做了个if (req.body.phone)就完事了。第三当一个校验逻辑在正则里写起来超过三行时果断改用代码。前面说的 IP 段范围、日期有效性、数字区间都是代码更容易表达的场景。正则的优势是简洁地表达“字符结构”不是“数值逻辑”。把正则用在它擅长的地方代码会优雅很多排错也会容易很多。我在实际项目中还养成了一个习惯所有复用的正则 pattern都放进一个集中目录比如constants/regex.js或者validation/patterns.py命名写清楚用途旁边加注释说明“为什么这样写”。这能避免三个不同模块各自复制了三份略有差异的手机号正则然后某天其中一份改了、另外两份没改导致同一个手机号在 A 接口能过、在 B 接口却被拦截的尴尬情况。集中管理看起来只是代码组织问题但落实下来能省掉的排查时间远超想象。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。