1

I have to create a regex that allows the user to input only a number (using . or ,)

so these examples are both valid:

  • 8,5
  • 8.5

here's my current code

private regex: RegExp = new RegExp(/^\d*[\,\.]{0,1}\d{1,2}/g);

However this allows me to input 8.,5 which is obviously bad. How can I change my regex so that the user can only place 1 of the decimal characters , OR .?

EDIT:

I've tried alot of answers, but most of them don't work (I can't place any decimal characters). Basically I'm creating a directive in angular that converts <input type="text"> to an numeric input (I can't use type="number")

Here's my directive code (see Angular2 - Input Field To Accept Only Numbers)

@Directive({
    selector: "[OnlyNumber]"
})
export class OnlyNumberDirective {
    // Allow decimal numbers. The \. is only allowed once to occur
    private regex: RegExp = new RegExp(/^(?=.+)\d*(?:[\,\.]\d{1,2})?$/g);
    // Allow key codes for special events. Reflect :
    // Backspace, tab, end, home
    private specialKeys: Array<string> = ["Backspace", "Tab", "End", "Home"];

    constructor(private el: ElementRef) {
    }

    @HostListener("keydown", ["$event"])
    onKeyDown(event: KeyboardEvent) {
        // Allow Backspace, tab, end, and home keys
        if (this.specialKeys.indexOf(event.key) !== -1) {
            return;
        }
        let current: string = this.el.nativeElement.value;
        let next: string = current.concat(event.key);
        if (next && !String(next).match(this.regex)) {
            event.preventDefault();
        }
    }
}

and here's how I use it in my template:

<mat-form-field class="numeric-textbox">
    <input matInput
           OnlyNumber
           #model="ngModel"
           placeholder="{{ label }}"
           [ngModel]="selectedValue"/>
    <mat-error><ng-content></ng-content></mat-error>
</mat-form-field>
5
  • 1
    ^\d*[,.]?\d{1,2}$ Commented Mar 12, 2018 at 13:59
  • 1
    @Red that's like applying a bandaid to a severed arm. Commented Mar 12, 2018 at 14:01
  • The comma (,) is not a special character in regex, there is no need to escape it. Inside a character set, the dot (.) doesn't have any special meaning, it represents itself; there is no need to escape it either. Commented Mar 12, 2018 at 14:10
  • Isn't that easier to replace , with . and then use parseFloat()? Commented Mar 12, 2018 at 14:13
  • new RegExp(/^\d*[\,\.]{0,1}\d{1,2}/g) is the same as /^\d*[\,\.]{0,1}\d{1,2}/g. Commented Mar 12, 2018 at 14:14

4 Answers 4

1

You should specify the end of input string with $ without which a partial match will happen. You shouldn't look for \d* unless you want to match values like .5 or ,5 otherwise they will match as a valid input.

^\d+(?:[.,]\d{1,2})?$

Note: You don't need to escape dots or commas inside a character class and a quantifier like [.,]{0,1} is literally equal to [.,]?

Live demo:

document.getElementById("number").addEventListener("keyup",function(e) {
  console.log(this.value.match(/^\d+(?:[.,]\d{1,2})?$/));
});
<input type="text" id="number" placeholder="Enter a number">

Update, based on comments

^(?![.,]?$)\d*[,.]?(?:\d{1,2})?$

This allows any number optionally followed or preceded by a decimal point or comma.

Live demo

Sign up to request clarification or add additional context in comments.

13 Comments

If they want to have .5 instead of 0.5, this regex would not allow that
That's what I said in answer. @KyleFairns
But why is that inherently wrong? Many do write decimals like that
I don't see them in OP's requirements as I don't see 8.5555 as part of it either. @KyleFairns
I just assumed that there would be a reason for them using a * instead of a +
|
0

The regex is correct, buy you just need to match the whole string: ^ start of the string $ end of the string

However, the regex can be improved with:

^   : for start of string
\d+ : for at least 1 digit
(
  [\,\.] : 1 comma
  \d{1,2} : followed by 1 digit or two
)?  : keep this optionnal. We can get numbers without commas
$  : end of string

Final regex may be:

/^\d+([\,\.]\d{1,2})?$/

2 Comments

this doesn't work for me, I can't place any decimal characters. I editted the OP to provide more info
/^\d+([\,\.]\d{1,2})?$/.test('1.2') (asnwer: true) Works for me on google chrome
0

Try: /^(?=.+)\d*(?:[,.]\d{1,2})?$/g

let regex = /^(?=.+)\d*(?:[,.]\d{1,2})?$/g,
strings = ["5", "50", "500", ".5", ",5","5.5","5,5", "5.55", "5,55", "5.555", "5,555", "5,,5", "5..5", "5,.5", "5.,5", "5,", "5."];

strings.forEach((string) => {
   console.log(`${regex} ${string.match(regex) ? `matches ${string.match(regex)}`: `has no match for ${string}`}`);
});

This will match:

From the start, lookahead to make sure that there are characters present (one or more), and then begin matching: any amount of digits (0 or more), and the following optional: a comma or a dot, and then 1 or 2 digits before the end of the string.

4 Comments

nope, the decimal is optional, you can place any number, (the maximum amount of decimals is 2, but it's not required)
@Nicolas would you still like .5 and ,5 to match?
it's a nice to have, but for the sake of this post it's not required
Thanks, but it's not working. Bassically I'm converting an <input type="text"> to a numbers only input (type="number" doesn't work because I need to be able to use both . and ,. With your regex it won't allow me to place any decimal signs. I'll update the OP, maybe it can help
0

Your regex is perfectly fine, you just need to specify the line's termination. You're currently matching 8 from 8,.5 which is likely why you're experiencing issues with your regex. I assume you're using JavaScript's test() function and getting true as a response for that string. Simply append $ to your regex and you'll get the correct answer. You can also simplify your regex to the following (also commented out in the snippet below). You can also probably drop the g flag as you're trying to match the string once, not multiple times:

^\d*[,.]?\d{1,2}$

What you're matching:

var a = ['8.5', '8,5', '.5', ',5', '8.,5']
var r = /^\d*[\,\.]{0,1}\d{1,2}/g

a.forEach(function(s){
  console.log(s.match(r))
})

What you should be matching:

var a = ['8.5', '8,5', '.5', ',5', '8.,5']
var r = /^\d*[\,\.]{0,1}\d{1,2}$/g
// var r = /^\d*[,.]?\d{1,2}$/

a.forEach(function(s){
  console.log(s.match(r))
})

2 Comments

@ctwheels you actually have /^\d*[\,\.]{0,1}\d{1,2}$/ in your test.
@PatrickRoberts yes, if you read what I said I simply changed the OP's original regex so that it works, but also provided a shorter, clearer example. My regex is commented out in the second snippet.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.