跳到主内容
d.devtul.fun
EN
正则 · UUID

UUID 正则

一份能直接抄走的 UUID 校验正则,外加版本位、变体位的讲解,以及正则到底能证明什么的实话。

正则模式

^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$

匹配标准的 8-4-4-4-12 十六进制带连字符形式。加 i 标志可忽略大小写。它只查形状,从不查版本语义。

在线测试这个正则

在下面直接改文本 —— 匹配在本机完成,不会上传任何内容。

其它写法

只认小写
^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$

拒掉大写字母,所以先归一化成小写,否则会拒掉严格生成器产出的大写 UUID。

只认 UUID v4
^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$

把版本位钉成 4、变体位钉成 8/9/a/b。只有你的系统确实只收 v4 时才用。

能直接抄走的 UUID 正则

标准形态是八个、四个、四个、四个、十二个十六进制数字,用连字符分隔:

^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$

这是最稳妥的通用检查:它接受大小写十六进制,强制五段各自的长度,并要求连字符恰好落在四个正确位置。加上 i 标志你可以省掉 A-F 部分,但显式写出来能让模式在忽略标志的引擎间也能移植。

每一段代表什么

  • [0-9a-fA-F]{8} —— 头 32 位,通常按版本不同是时间戳的低或高部分。
  • {4} —— 接下来 16 位,历史上放与版本相关的时间字段。
  • {4} —— 版本位就坐在这段的第一个字符(见下)。
  • {4} —— 变体位就坐在这段的第一个字符。
  • {12} —— 最后 48 位,通常是节点(MAC 或随机值)。

连字符不是装饰。它们是 RFC 4122 / 9562 文本表示的一部分,位置本身 encodes 了每个字段的边界落在哪儿。

版本位与变体位,正则可以钉死的部分

UUID 不只是三十二个十六进制字符;有两个特定位置承载着含义:

  • 版本号是第三段(第三组)的第一个十六进制字符(从 0 数第 14 位)。目前合法值是 1 到 8:v1 基于时间、v2 DCE、v3 和 v5 基于名字(MD5/SHA-1)、v4 随机、v6/v7 时间有序、v8 自定义。
  • 变体位是第四段(第四组)的第一个十六进制字符(第 19 位)。在 RFC 4122 布局下它必须是 8、9、a 或 b——也就是最高两位是 10。

如果你只收 v4,就把这两个位置钉死:

^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$

第二个连字符后的 4 强制版本为 4;第三个连字符后的 [89abAB] 强制合法的 RFC 变体。

为什么正则只能查形状

这是本页核心论点。正则只能确认一个字符串看起来像 UUID。它无法确认版本语义被遵守了。一个字符串版本位写了 4,可能只是碰巧,背后零位随机性——对正则"完全合法",密码学上却一文不值。反过来,未来的版本 9 会被今天钉死版本的正则拒掉,即便它本是个正当 UUID。正则看的是数字,看不懂含义。

常见错误写法

  • 丢了大小写:只写 [0-9a-f] 会拒掉大写,于是 550E8400-... 失败。要么补上 A-F,要么先归一化大小写。
  • 段数写错:漏掉一段、或在某块写成 {16},会得到匹配错长度、悄悄放过垃圾的模式。
  • 用了 \w:\w 匹配数字、字母,还匹配下划线,于是 ____-____-____-____-________ 也能过。永远用显式十六进制类。
  • 丢了锚点:没有 ^ 和 $,1234-... 嵌在更长的串里也会被当子串匹配,骗过校验器。

真例子:放行与拒绝

输入结果原因
550e8400-e29b-41d4-a716-446655440000放行正确的 8-4-4-4-12 形态,合法 v4
550E8400-E29B-41D4-A716-446655440000放行大写同样是合法十六进制
123e4567-e89b-12d3-a456-426614174000放行合法的 v1 形态
not-a-uuid拒绝不是十六进制,没有结构
12345678-1234-1234-1234-1234567890拒绝最后一段只有 10 位,需要 12 位

花括号、URN 前缀与 nil UUID

真实输入比标准形态更脏:

  • 微软工具爱把 UUID 包进花括号:{550e8400-e29b-41d4-a716-446655440000}。测试前先去掉花括号,或者扩展模式放行。
  • URN 形态带前缀:urn:uuid:550e8400-e29b-41d4-a716-446655440000。同样,先去掉或放行前缀。
  • nil UUID 00000000-0000-0000-0000-000000000000 在语法上合法,意思是"没有 UUID"。把它当哨兵值,别当真标识符用。

该用什么替代

要做真正的校验,去解析而不是去匹配。多数语言自带 UUID 类型,会拒绝畸形输入,甚至检查版本一致性:

import uuid
try:
    u = uuid.UUID("550e8400-e29b-41d4-a716-446655440000")
    print(u.version)   # 4
except ValueError:
    print("不是 uuid")

JavaScript 里 crypto.randomUUID() 产出 v4 字符串,new UUID(...)(或一个小库)负责解析。用解析器:它对 nil、版本、变体的理解远胜你写的任何正则。

生成与校验是两件事

把这两件事分开值得:生成 UUID 应当来自密码学安全的随机源(大多数标准库已经这样做了),而校验 UUID 才是本页的主题。正则是可能最弱的校验器,解析器是最强的。

千万别自己手搓随机十六进制、再指望版本位碰巧写对——调用库的生成器,版本位和变体位会替你设好。如果你只是想在数据库里放一个主键,直接拿库生成的字符串,存之前用解析器过一遍,比任何正则都省心。记住:能被正则放行的字符串千千万,其中只有真正由生成器产出的才该被信任;其余碰巧符合形态的,不过是长得像而已。把信任建立在解析器而不是形状上,是写对这块代码最便宜的一课。

常见问题

正确的 UUID 正则是什么?

通用形态用 ^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$,需要的话再钉死版本位。

正则能确认 UUID 真的是 v4 吗?

只能表面确认。它能确认版本位是 4,却确认不了其余部分真随机。真正的检查交给 UUID 解析器。

为什么不该用 \w 表示十六进制?

\w 还会匹配下划线,于是一串下划线也能过。永远用显式的 [0-9a-fA-F] 类。

花括号和 urn:uuid: 前缀怎么处理?

匹配前先去掉外层花括号或 urn:uuid: 前缀,或者扩展模式放行它们。

其它正则速查

打开完整的正则测试器