PatternRegex

Match a UUID v4

Match version-4 UUIDs precisely: this pattern checks the version digit and variant bits, not just the 8-4-4-4-12 dash layout.

Last updated

Patterng
[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}
Open in Regex Tester & Builder

Matches

  • 9b2d8e1a-3f4c-4d5e-9f6a-7b8c9d0e1f2a
  • AB12CD34-EF56-4A7B-8C9D-0E1F2A3B4C5D

Doesn’t match

  • 12345678-1234-1234-1234-123456789012
  • 9b2d8e1a3f4c4d5e9f6a7b8c9d0e1f2a

Why it’s written this way

The third block must start with 4 — that IS the version field. The fourth block must start with 8, 9, a or b, which is the RFC 4122 variant. Checking both is what distinguishes "a real v4 UUID" from "32 hex digits wearing dashes".

Everything else is plain hex in the standard 8-4-4-4-12 grouping.

Edge cases to know

  • →Other UUID versions (v1 timestamps, v5 hashes, v7) deliberately fail — drop the two literal checks if you want any-version matching.
  • →Case-insensitive by character class rather than the i flag, so it behaves the same wherever you paste it.
  • →The all-zero nil UUID does not match — its version digit is 0.

Part by part

[0-9a-fA-F]{8}
The first group: eight hex digits. Both letter cases are listed, so the pattern works without the i flag and stays predictable inside a larger expression.
4[0-9a-fA-F]{3}
The third group, and the reason this is a v4 pattern rather than a generic one. That literal 4 is the version nibble — every UUID v4 has it, and no other version does.
[89abAB][0-9a-fA-F]{3}
The fourth group. The first character is restricted to 8, 9, a or b because the two top bits of that nibble are the RFC 4122 variant marker.
[0-9a-fA-F]{12}
The final group: twelve hex digits, the longest run in the format.
-
The literal hyphens separating the five groups into the familiar 8-4-4-4-12 shape.

Common mistakes

  • [0-9a-fA-F-]{36}

    Counting to 36 characters accepts any mix of hex digits and hyphens of that length, including a string of 36 hyphens, and rejects nothing that matters. It is shape-blind in the one place shape is the whole point.

    [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}
  • [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}

    Dropping the version and variant constraints turns this into a pattern for any UUID. That is a legitimate thing to want, but it will then match v1 identifiers, which encode a MAC address and a timestamp and have very different privacy properties.

  • [0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}

    Lowercase-only is fine if you generated the value yourself, but .NET and several databases render UUIDs in uppercase by default, so this silently fails on data that arrives from those systems.

Questions

What does the literal 4 in the third group mean?

It is the version number. UUIDs carry their version in that position, and 4 means the value was generated from random data rather than from a timestamp or a name. Removing it makes the pattern match any UUID version.

Why is the fourth group restricted to 8, 9, a and b?

Those four hex digits are the ones whose binary form starts with the bits 10, which is the RFC 4122 variant marker. A UUID with anything else there was not generated to that specification, whatever else it looks like.

Does a match mean the UUID is genuinely random?

No. The pattern reads the version and variant markers, which any generator can write. A poor implementation using a weak random source produces values that look identical to good ones — that is a property of how it was made, not of how it reads.

Should I store UUIDs as text if I am matching them this way?

Usually not. A native UUID column halves the storage against a 36-character string and indexes better, and databases that offer one will format it back into this shape on the way out — so you can keep this pattern for logs and inputs while the storage stays compact.

Related in Patterns