Nicolas
Nicolas

Reputation: 4756

Regex - Numeric regex with . and ,

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

so these examples are both valid:

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>

Upvotes: 1

Views: 219

Answers (4)

revo
revo

Reputation: 48711

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

Upvotes: 1

ctwheels
ctwheels

Reputation: 22817

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))
})

Upvotes: 0

kfairns
kfairns

Reputation: 3057

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.

Upvotes: 0

tfe
tfe

Reputation: 60

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})?$/

Upvotes: 0

Related Questions