The pattern that you tried, consists of all optional parts because all the quantifiers are either ?
for optional, or *
for 0 or more times. As the /
is also optional, you will match 12.12.12
You could make the part with the /
part optional without making the /
itself optional.
If you want to match at least a digit and prevent matching empty strings you can use a positive lookahead to match a least a digit.
^(?=[^\d\r\n]*\d)\d*\.?\d*(?:\/\d*\.?\d*)?[a-z]*$
Regex demo
The pattern matches
^
Start of string
(?=[^\d\r\n]*\d)
Assert at least a digit
\d*\.?\d*
Match optional digits and .
(?:
Non capture group
\/\d*\.?\d*
Match /
and optional digits and .
)?
Close non capture group and make it optional
[a-z]*
Optionally match chars a-z
$
End of string
If there should be at least a digit on each side of the /
to not match for example ./.
^(?:\d*\.?\d+|\d+\.)(?:\/\d*\.?\d+|\d+\.)?[a-z]*$
The pattern matches
^
Start of string
(?:\d*\.?\d+|\d+\.)
Match either optional digits, optional .
and 1+ digits OR match 1+ digits and .
(?:
Non capture group
\/\d*\.?\d+|\d+\.
Match /
and the same as the first pattern again
)?
Close non capture group and make it optional
[a-z]*
Optionally match chars a-z
$
End of string
Regex demo