Utilo

Semver Regular Expression – Free, Online, Private

Matches a practical MAJOR.MINOR.PATCH semver, with an optional prerelease and build.

Tester
Sample
Matches (3)
  • 1.2.3
  • 1.2.3-alpha.1
  • 1.2.3+build.5

The pattern

The pattern matches 1.2.3, a prerelease such as 1.2.3-alpha.1, and a build such as 1.2.3+build.5. Each numeric part may be 0 or a number that does not start with 0. 01.2.3 does not match. A leading v does not match. This is a readable subset of the grammar on semver.org. It does not split prerelease identifiers to enforce every identifier rule. It is the check to use before you hand the string to a real semver library.

The source is ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$. The flags on this page are m. i means case-insensitive. m means ^ and $ match at line breaks, which is how the sample can show several candidates. g is not set. The tester finds non-overlapping matches, and without g a pattern with no anchor still reports the matches it walks. The box under the title is a tester with this pattern and the sample already filled in. Edit either one. Nothing you type is uploaded. The same tester, without a preset, is the regex tool.

How to read it

(0|[1-9]\d*) is one numeric identifier: either a single 0 or a non-zero digit followed by any digits. It is repeated three times with literal dots. (?:-[0-9A-Za-z.-]+)? is an optional hyphen and a prerelease run. (?:\+[0-9A-Za-z.-]+)? is an optional plus and a build run. The plus is escaped because + is a quantifier. Anchors plus the m flag test each line of the sample. Drop m when the input is a single version.

What it will not do

The official grammar limits prerelease identifiers more tightly than this class does. A prerelease of a single dot would fail here because the class still needs a character, but 1.2.3-.1 matches this pattern and is not legal semver. If you are publishing versions, use a library. If you are highlighting versions in a changelog, this pattern is the right size. Leading v is common in git tags and is deliberately rejected so a tag and a version are not confused.

A match is not a parsed value. An email pattern does not send mail, a UUID pattern does not allocate an id, and a password pattern does not store a password. When the next step needs a real parser, use one: the URL constructor, a date library, a semver library, or the converters on this site. The expression is the part you can see.

JavaScript, Python, and Go

The JavaScript snippet uses the RegExp constructor so the source does not have to be escaped for a literal slash. The Python snippet uses a normal string with the same escapes JSON would emit, which keeps \d as a regex digit class. Go's snippet uses a raw string. If the pattern contains a lookahead, the Go snippet says so and does not call MustCompile, because RE2 would reject it at runtime.

JavaScriptconst re = new RegExp("^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$", "m");
const found = text.match(re);
console.log(found ? found[0] : "no match");
Pythonimport re

pattern = re.compile("^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$", re.MULTILINE)
found = pattern.search(text)
print(found.group(0) if found else "no match")
Gopackage main

import (
	"fmt"
	"regexp"
)

func main() {
	text := ""
	re := regexp.MustCompile(`(?m)^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$`)
	fmt.Println(re.FindString(text))
}

Frequently asked questions

Does v1.2.3 match?

No. The pattern is the version, not a git tag. Strip a leading v first.

Are leading zeros allowed?

No. 01.2.3 fails. 0.1.0 succeeds because the zero is a single 0.

Does 1.2.3-alpha.1 match?

Yes. The prerelease group is optional and accepts that shape.

Is this the full semver grammar?

No. It is a practical subset. A library should be the publisher's check.