Regular expression for digits, decimals, and fractions

Viewed 411

How can I create a regex that allows whole numbers, decimals, fractions, and fractions with decimals? The string can also have optional text, only at the end. So far I have this:

const re = /^\d*\.?\d*\/?\d*\.?\d*[a-z]*$/gi;

This allows double decimals in a whole number(ie: '23.23.23'), which I do not want. Can I modify this regex to allow two decimal points only if it is separated by a '/'?

Here are some examples that can pass:

  • 23.23/100km
  • 1/3
  • .23km
  • 1.mi
  • 1.2/2.1kg

Some examples that shouldn't pass:

  • 1a3km
  • 12.12.12
  • 1.2.3/12.13km
  • 12km/12.44km
3 Answers

Use

^(?!.*\d+(?:\.\d+){2})\d*\.?\d*\/?\d*\.?\d*[a-z]*$

See proof. This expression disallows three numbers that have a period between one another thanks to (?!.*\d+(?:\.\d+){2}) negative lookahead.

EXPLANATION

--------------------------------------------------------------------------------
  ^                        the beginning of the string
--------------------------------------------------------------------------------
  (?!                      look ahead to see if there is not:
--------------------------------------------------------------------------------
    .*                       any character except \n (0 or more times
                             (matching the most amount possible))
--------------------------------------------------------------------------------
    \d+                      digits (0-9) (1 or more times (matching
                             the most amount possible))
--------------------------------------------------------------------------------
    (?:                      group, but do not capture (2 times):
--------------------------------------------------------------------------------
      \.                       '.'
--------------------------------------------------------------------------------
      \d+                      digits (0-9) (1 or more times
                               (matching the most amount possible))
--------------------------------------------------------------------------------
    ){2}                     end of grouping
--------------------------------------------------------------------------------
  )                        end of look-ahead
--------------------------------------------------------------------------------
  \d*                      digits (0-9) (0 or more times (matching
                           the most amount possible))
--------------------------------------------------------------------------------
  \.?                      '.' (optional (matching the most amount
                           possible))
--------------------------------------------------------------------------------
  \d*                      digits (0-9) (0 or more times (matching
                           the most amount possible))
--------------------------------------------------------------------------------
  \/?                      '/' (optional (matching the most amount
                           possible))
--------------------------------------------------------------------------------
  \d*                      digits (0-9) (0 or more times (matching
                           the most amount possible))
--------------------------------------------------------------------------------
  \.?                      '.' (optional (matching the most amount
                           possible))
--------------------------------------------------------------------------------
  \d*                      digits (0-9) (0 or more times (matching
                           the most amount possible))
--------------------------------------------------------------------------------
  [a-z]*                   any character of: 'a' to 'z' (0 or more
                           times (matching the most amount possible))
--------------------------------------------------------------------------------
  $                        before an optional \n, and the end of the
                           string

This should work

^[0-9]*(?:\.[0-9]*)?(?:\/[0-9]*(?:\.[0-9]*)?)?[a-zA-Z]*$

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

Related