I am after a regular expression that validates a percentage from 0 100 and allows two decimal places.
Does anyone know how to do this or know of good web site that has example of common regular expressions used for client side validation in javascript?
#Tom - Thanks for the questions. Ideally there would be no leading 0's or other trailing characters.
Thanks to all those who have replied so far. I have found the comments really interesting.
Rather than using regular expressions for this, I would simply convert the user's entered number to a floating point value, and then check for the range you want (0 to 100). Trying to do numeric range validation with regular expressions is almost always the wrong tool for the job.
var x = parseFloat(str);
if (isNaN(x) || x < 0 || x > 100) {
// value is out of range
}
I propose this one:
(^100(\.0{1,2})?$)|(^([1-9]([0-9])?|0)(\.[0-9]{1,2})?$)
It matches 100, 100.0 and 100.00 using this part
^100(\.0{1,2})?$
and numbers like 0, 15, 99, 3.1, 21.67 using
^([1-9]([0-9])?|0)(\.[0-9]{1,2})?$
Note what leading zeros are prohibited, but trailing zeros are allowed (though no more than two decimal places).
This reminds me of an old blog Entry By Alex Papadimoulis (of The Daily WTF fame) where he tells the following story:
"A client has asked me to build and install a custom shelving system. I'm at the point where I need to nail it, but I'm not sure what to use to pound the nails in. Should I use an old shoe or a glass bottle?"
How would you answer the question?
It depends. If you are looking to pound a small (20lb) nail in something like drywall, you'll find it much easier to use the bottle, especially if the shoe is dirty. However, if you are trying to drive a heavy nail into some wood, go with the shoe: the bottle with shatter in your hand.
There is something fundamentally wrong with the way you are building; you need to use real tools. Yes, it may involve a trip to the toolbox (or even to the hardware store), but doing it the right way is going to save a lot of time, money, and aggravation through the lifecycle of your product. You need to stop building things for money until you understand the basics of construction.
This is such a question where most people sees it as a challenge to come up with the correct regular expression to solve the problem, but it would be much better to just say that using regular expressions are using the wrong tool for the job.
The problem when trying to use regex to validate numeric ranges is that it is hard to change if the requirements for the allowed range is changes. Today the requirement may be to validate numbers between 0 and 100 and it is possible to write a regex for that which doesn't make your eyes bleed. But next week the requirment maybe changes so values between 0 and 315 are allowed. Good luck altering your regex.
The solution given by Greg Hewgill is probably better - even though it would validate "99fxx" as "99". But given the circumstances that might actually be ok.
Given that your value is in str
str.match(/^(100(\.0{1,2})?|([0-9]?[0-9](\.[0-9]{1,2})))$/)
^100(\.(0){0,2})?$|^([1-9]?[0-9])(\.(\d{0,2}))?\%$
This would match:
100.00
optional "1-9" followed by a digit (this makes the int part), optionally followed by a dot and two digits
From what I see, Greg Hewgill's example doesn't really work that well because parseFloat('15x') would simply return 15 which would match the 0<x<100 condition. Using parseFloat is clearly wrong because it doesn't validate the percentage value, it tries to force a validation. Some people around here are complaining about leading zeroes and some are ignoring trailing invalid characters. Maybe the author of the question should edit it and make clear what he needs.
I recomend this, if you are not exclusively developing for english speaking users:
[0-9]{1,2}((,|\.)[0-9]{1,10})?%?
You can simply replace the 10 by a 2 to get two decimal places.
My example will match:
15.5
5.4366%
1,43
50,55%
34
45%
Of cause the output of this one is harder to cast, but something like this will do (Java Code):
private static Double getMyVal(String myVal) {
if (myVal.contains("%")) {
myVal = myVal.replace("%", "");
}
if (myVal.contains(",")) {
myVal = myVal.replace(',', '.');
}
return Double.valueOf(myVal);
}
None of the above solutions worked for me, as I needed my regex to allow for values with numbers and a decimal while the user is typing ex: '18.'
This solution allows for an empty string so the user can delete their entire input, and accounts for the other rules articulated above.
/(^$)|(^100(\.0{1,2})?$)|(^([1-9]([0-9])?|0)\.(\.[0-9]{1,2})?$)|(^([1-9]([0-9])?|0)(\.[0-9]{1,2})?$)/
(100|[0-9]{1,2})(\.[0-9]{1,2})?
That should be the regex you want. I suggest you to read Mastering Regular Expression and download RegexBuddy or The Regex Coach.
#mlarsen:
Is not that a regex here won't do the job better.
Remember that validation msut be done both on client and on server side, so something like:
100|(([1-9][0-9])|[0-9])(\.(([0-9][1-9])|[1-9]))?
would be a cross-language check, just beware of checking the input length with the output match length.
(100(\.(0){1,2})?|([1-9]{1}|[0-9]{2})(\.[0-9]{1,2})?)
Related
This is an extension of this SO question
I made a function to see if i can correctly format any number. The answers below work on tools like https://regex101.com and https://regexr.com/, but not within my function(tried in node and browser):
const
const format = (num, regex) => String(num).replace(regex, '$1')
Basically given any whole number, it should not exceed 15 significant digits. Given any decimal, it should not exceed 2 decimal points.
so...
Now
format(0.12345678901234567890, /^\d{1,13}(\.\d{1,2}|\d{0,2})$/)
returns 0.123456789012345678 instead of 0.123456789012345
but
format(0.123456789012345,/^-?(\d*\.?\d{0,2}).*/)
returns number formatted to 2 deimal points as expected.
Let me try to explain what's going on.
For the given input 0.12345678901234567890 and the regex /^\d{1,13}(\.\d{1,2}|\d{0,2})$/, let's go step by step and see what's happening.
^\d{1,13} Does indeed match the start of the string 0
(\. Now you've opened a new group, and it does match .
\d{1,2} It does find the digits 1 and 2
|\d{0,2} So this part is skipped
) So this is the end of your capture group.
$ This indicates the end of the string, but it won't match, because you've still got 345678901234567890 remaining.
Javascript returns the whole string because the match failed in the end.
Let's try removing $ at the end, to become /^\d{1,13}(\.\d{1,2}|\d{0,2})/
You'd get back ".12345678901234567890". This generates a couple of questions.
Why did the preceding 0 get removed?
Because it was not part of your matching group, enclosed with ().
Why did we not get only two decimal places, i.e. .12?
Remember that you're doing a replace. Which means that by default, the original string will be kept in place, only the parts that match will get replaced. Since 345678901234567890 was not part of the match, it was left intact. The only part that matched was 0.12.
Answer to title question: your function doesn't replace, because there's nothing to replace - the regex doesn't match anything in the string. csb's answer explains that in all details.
But that's perhaps not the answer you really need.
Now, it seems like you have an XY problem. You ask why your call to .replace() doesn't work, but .replace() is definitely not a function you should use. Role of .replace() is replacing parts of string, while you actually want to create a different string. Moreover, in the comments you suggest that your formatting is not only for presenting data to user, but you also intend to use it in some further computation. You also mention cryptocurriencies.
Let's cope with these problems one-by-one.
What to do instead of replace?
Well, just produce the string you need instead of replacing something in the string you don't like. There are some edge cases. Instead of writing all-in-one regex, just handle them one-by-one.
The following code is definitely not best possible, but it's main aim is to be simple and show exactly what is going on.
function format(n) {
const max_significant_digits = 15;
const max_precision = 2;
let digits_before_decimal_point;
if (n < 0) {
// Don't count minus sign.
digits_before_decimal_point = n.toFixed(0).length - 1;
} else {
digits_before_decimal_point = n.toFixed(0).length;
}
if (digits_before_decimal_point > max_significant_digits) {
throw new Error('No good representation for this number');
}
const available_significant_digits_for_precision =
Math.max(0, max_significant_digits - digits_before_decimal_point);
const effective_max_precision =
Math.min(max_precision, available_significant_digits_for_precision);
const with_trailing_zeroes = n.toFixed(effective_max_precision);
// I want to keep the string and change just matching part,
// so here .replace() is a proper method to use.
const withouth_trailing_zeroes = with_trailing_zeroes.replace(/\.?0*$/, '');
return withouth_trailing_zeroes;
}
So, you got the number formatted the way you want. What now?
What can you use this string for?
Well, you can display it to the user. And that's mostly it. The value was rounded to (1) represent it in a different base and (2) fit in limited precision, so it's pretty much useless for any computation. And, BTW, why would you convert it to String in the first place, if what you want is a number?
Was the value you are trying to print ever useful in the first place?
Well, that's the most serious question here. Because, you know, floating point numbers are tricky. And they are absolutely abysmal for representing money. So, most likely the number you are trying to format is already a wrong number.
What to use instead?
Fixed-point arithmetic is the most obvious answer. Works most of the time. However, it's pretty tricky in JS, where number may slip into floating-point representation almost any time. So, it's better to use decimal arithmetic library. Optionally, switch to a language that has built-in bignums and decimals, like Python.
I need to match a number range:
-9223372036854775808 to 9223372036854775807
^(?:922337203685477580[0-7]|9223372036854775[0-7]\d{2}|922337203685477[0-4]\d{3}|92233720368547[0-6]\d{4}|9223372036854[0-6]\d{5}|922337203685[0-3]\d{6}|92233720368[0-4]\d{7}|9223372036[0-7]\d{8}|922337203[0-5]\d{9}|92233720[0-2]\d{10}|922337[0-1]\d{12}|92233[0-6]\d{13}|9223[0-2]\d{14}|922[0-2]\d{15}|92[0-1]\d{16}|9[01]\d{17}|[1-8]\d{18}|\d{0,18}|-(?:922337203685477580[0-8]|9223372036854775[0-7]\d{2}|922337203685477[0-4]\d{3}|92233720368547[0-6]\d{4}|9223372036854[0-6]\d{5}|922337203685[0-3]\d{6}|92233720368[0-4]\d{7}|9223372036[0-7]\d{8}|922337203[0-5]\d{9}|92233720[0-2]\d{10}|922337[0-1]\d{12}|92233[0-6]\d{13}|9223[0-2]\d{14}|922[0-2]\d{15}|92[0-1]\d{16}|9[01]\d{17}|[1-8]\d{18}|\d{0,18}))?$
// space for easier copy and paste
Yes, I know it sounds crazy, but there's a long story behind this. I can't figure out how to do this in JavaScript by just checking a range, because of the size of the number, and this must be accurate.
Here's the thought process in breaking this thing down. I just started with the max number and worked my way down, then worked on the negative by just adding the - in the regex. You'll obviously have to copy and paste this thing somewhere to see it all. Also, could be mistakes. Made my head nearly explode.
9,223,372,036,854,775,807
922337203685477580[0-7]
9223372036854775[0-7][0-9]{2}
922337203685477[0-4][0-9]{3}
92233720368547[0-6][0-9]{4}
9223372036854[0-6][0-9]{5}
922337203685[0-3][0-9]{6}
92233720368[0-4][0-9]{7}
9223372036[0-7][0-9]{8}
922337203[0-5][0-9]{9}
92233720[0-2][0-9]{10}
922337[0-1][0-9]{12}
92233[0-6][0-9]{13}
9223[0-2][0-9]{14}
922[0-2][0-9]{15}
92[0-1][0-9]{16}
9[01][0-9]{17}
[1-8][0-9]{18}
[0-9]{0,18}
There's a single digit different in the negative vs. positive, so you'll see where I had to basically duplicate most of this.
So a few question:
Did I do this right?
If not, what's a better way?
Can this be done without regular expressions considering the size of the number? I need to validate client-side.
Can it be refactored and still retain strict rules?
Suggestions appreciated :)
Can this be done without regular expressions considering the size of the number?
It can be done in a series of if statements using only string operations (no need to convert to numbers).
all strings that don't match [0-9]{1,19} are out
all candidates that are of length 18 or less are good
for length 19 you can work with string comparison to see if they are numerically less than your upper limit
tweak the above to take care of negative numbers
Your regex is correct.
This is a shorter version
^(?:-9223372036854775808|-?(?:\d{0,18}|(?!922337203685477580[8-9]|92233720368547758[1-9]|92233720368547759|922337203685477[6-9]|92233720368547[8-9]|9223372036854[8-9]|922337203685[5-9]|92233720368[6-9]|92233720369|922337203[7-9]|92233720[4-9]|9223372[1-9]|922337[3-9]|92233[8-9]|9223[4-9]|922[4-9]|92[3-9]|9[3-9])\d{19}))$
Regex demo
How to generate that regex without mistake:
Input max number:
9223372036854775807
Output:
9223372036854775807
922337203685477580
92233720368547758
9223372036854775
922337203685477
92233720368547
9223372036854
922337203685
92233720368
9223372036
922337203
92233720
9223372
922337
92233
9223
922
92
9
Replace last number letter
9->remove all line
8->9
7->[8-9]
6->[7-9]
5->[6-9]
4->[5-9]
3->[4-9]
2->[3-9]
1->[2-9]
0->[1-9]
Output:
922337203685477580[8-9]
92233720368547758[1-9]
92233720368547759
922337203685477[6-9]
92233720368547[8-9]
9223372036854[8-9]
922337203685[5-9]
92233720368[6-9]
92233720369
922337203[7-9]
92233720[4-9]
9223372[1-9]
922337[3-9]
92233[8-9]
9223[4-9]
922[4-9]
92[3-9]
9[3-9]
Regex [Output]
922337203685477580[8-9]|92233720368547758[1-9]|92233720368547759|922337203685477[6-9]|92233720368547[8-9]|9223372036854[8-9]|922337203685[5-9]|92233720368[6-9]|92233720369|922337203[7-9]|92233720[4-9]|9223372[1-9]|922337[3-9]|92233[8-9]|9223[4-9]|922[4-9]|92[3-9]|9[3-9]
Add these[output] to regex
(?!output)\d{19}
Will become [output2]
(?!922337203685477580[8-9]|92233720368547758[1-9]|92233720368547759|922337203685477[6-9]|92233720368547[8-9]|9223372036854[8-9]|922337203685[5-9]|92233720368[6-9]|92233720369|922337203[7-9]|92233720[4-9]|9223372[1-9]|922337[3-9]|92233[8-9]|9223[4-9]|922[4-9]|92[3-9]|9[3-9])\d{19}
Matches \d{19} <= 9223372036854775807
Add
^(?:-9223372036854775808|-?(?:\d{0,18}|[output2]))$
^(?:-9223372036854775808|-?(?:\d{0,18}|(?!922337203685477580[8-9]|92233720368547758[1-9]|92233720368547759|922337203685477[6-9]|92233720368547[8-9]|9223372036854[8-9]|922337203685[5-9]|92233720368[6-9]|92233720369|922337203[7-9]|92233720[4-9]|9223372[1-9]|922337[3-9]|92233[8-9]|9223[4-9]|922[4-9]|92[3-9]|9[3-9])\d{19}))$
Will match
-9223372036854775808 or
+/- \d{0,18} or
+/- \d{19} <= 9223372036854775807
Demo
Im designing a conversion website where i perform calculations on inputted numbers and i need my input or textarea to receive and interpret numbers entered in different fashions
like:
Entry = 3,000,000.1111
Interpreted value = 3000000.1111
or
Entry = 3000000.1111
Interpreted value = 3000000.1111
and I want to include a second input for European decimal notation
(or if possible have the same input do both)
Entry = 3.000.000,1111 (comma acts a decimal, decimal as separator)
Interpreted value = 3000000.1111
I wonder how I could do this. I suspect from some of my research that I could use regex.
Also should i use an input or a textarea? I want to limit the size of the number to 40 places.
It seems the textarea Im currently using won't recognize any values after a comma when a comma is used. I realized this is due to parseFloat. So I need to remove the commas using .replace() before parsing. But what do I do in the instance of European notation where the comma IS the decimal point? I suspect I should use regex to identify if a number is in comma decimal notation or standard decimal point notation and then outline the appropriate replacement behavior based on that. Any ideas how to write regex to identify a number between .0000000001 and 1,000,000,000,000,000 by only the separator and decimal point? What about when the entry doesn't use either? 12000 for example. Any help with this would be appreciated. Using HTML5 and Javascript. I am not using a form and am new at this. This is my first web page so please be patient with my questions.
I was thinking about this:
input = //value from textarea as a string
if(/REGEX which determines that the structure of the number is N,NNN.NN/.test(input)){
input = input.replace(/\,/,""); //replace the commas with nothing
}
else if(/REGEX which determine that structure of the number is N.NNN,NN/.test(input){
input = input.replace(/\./,""); //replace the decimal point separators with nothing
input = input.replace(/\,/,".");//replace the comma decimal with a point decimal
}
else{
//input unchanged assuming is NNNN without decimal
}
number = parseFloat(input);
I want to keep the possibility open for them to enter large numbers and also to use numbers less than one to 10 decimal places. Thanks to those who contributed.
Best,RP
I believe this should handle everything:
^[1-9](?:\d{0,2}(?:([,.])\d{3})*|\d+)(?:(?!\1)[,.]\d+)?$
You're treading on complicated territory here. Also, the above RegEx does not allow for values less than "1".
Basically, the RegEx does the following:
Allows for no thousandths separators ("," or ".") but ensures if they are used that they occur in the correct places.
Allows for either "," or "." to be used as both thousandths/cents separators, but ensures that the cents separator is not the same as the thousandths separator.
Requires the string equivalent number to begin with any digit other than "0".
To implement this you could attach an event listener to your form element(s) and use JS to do a simple .test.
After reading further, I think I misinterpreted your goal originally. I assumed you simply wanted to validate these values with a RegEx. I also assumed you're trying to work with currency (ie. two decimal places). However, fret not! You can still utilize my original answer if you really want.
You mentioned input and textarea which are both form elements. You can attach a listener to these element(s) looking for the input, change, and/or keyup events. As a part of the callback you can run the .test method or some other functionality. Personally, I would rethink how you want to handle input. Also, what's your actual goal here? Do you really need to know the thousandths separator or keep track of it? Why not just disallow any characters other than the one decimal point/comma and digits?
Also, parsing numbers like .0000000001 as a float is a terrible idea. You will lose precision very quickly if you do any sort of calculations such as multiplication, division, power, etc. You're going to have to figure out a different method to do this like storing the number to the right separately and as integers instead then go from there.
I can help you if you describe what you're trying to do in better detail.
I have a javascript chat bot where a person can type into an input box any question they like and hope to get an accurate answer. I can do this but I know I'm going about this all wrong because I don't know what position the number will appear in the sentence. If a person types in exactly:
what's the square root of 5 this works fine.
If he types in things like this it doesn't.
what is the square root of the 5
the square root of 5 is what
do you know what the square root of 5 is
etc
I need to be able to determine where the number appears in the sentence then do the calculation from there. Note the line below is part of a bigger working chatbot. In the line below I'm just trying to be able to answer any square root question regardless of where the number appears in the sentence. I also know there are many pitfalls with an open ended input box where a person can type anything such as spelling errors etc. This is just for entertainment not a serious scientific project. :)
if(
(word[0]=="what's") &&
(word[1]=="the") &&
(word[2]=="square") &&
(word[3]=="root") &&
(word [4]=="of") &&
(input.search(/\d{1,10}/)!=-1) &&
(num_of_words==6)
){
var root= word[5];
if(root<0){
document.result.result.value = "The square root of a negative number is not possible.";
}else{
word[5] = Math.sqrt(root);
word[5] = Math.round(word[5]*100)/100
document.result.result.value = "The square root of "+ root +" is "+ word[5] +".";
}
return true;
}
Just to be clear the bot is written using "If statemments" for a reason. If the input in this case doesn't include the words "what" and "square root" and "some number" the line doesn't trigger and is answered further down by the bot with a generic "I don't know type of response". So I'm hoping any answer will fit the format I am using. Be kind, I'm new here. I like making bots but I'm not much of a programmer. Thanks.
You can do this using a regular expression.
"What is the sqrt of 5?".match(/\d+/g);
["5"]
The output is an array containing all of the numbers found in the string. If you have more than one, like "Is the square root of 100 10?" then it will be
"Is the square root of 100 10?".match(/\d+/g);
["100", "10"]
so you can pick what number you want to use in your script.
You will need to use regular expressions, if you do not know what they are you should look them up as it would take too long to explain them in this response. Here is a useful website for regular expressions in JavaScript https://developer.mozilla.org/en/JavaScript/Guide/Regular_Expressions.
Assuming that you know regular expressions you should first search for numbers and store all the numbers that you find, if no numbers are found print out an error message. As an added note, you may what to consider searching for mathematical constants such as pi or e. This should work
nums = someString./\d+|pi|e/gi
The next part is going to be hard but to boil it down to is core concept, you need to look for key words such as 'square root', 'times', or 'plus'. You should do this word by word going left to right. For example if a user inputs
What is 5 plus 3 minus 8?
you should detect the plus before the minus, while if this is inputted
What is 5 minus 3 plus 8?
You should detect the minus before the plus.
For operations that uses two numbers you need to take the first two numbers that you found and do the operation and replace the two numbers with the result. I am trying to use reverse polish notation if you do not quite understand what I am trying to do, look it up if do not know what it is.
I hope I understood your question correctly and provided some help to coming to a solution because what you asked is very hard but seems like fun. Good luck. Also as a warning I am not considering order of operations in my response.
I'm trying to match the following, and i'm having a difficult time doing so.
I want to allow 0 to 100, or 0% to 100%. In my textfield, i strip the % out, so if they put in 100, it won't fail at the regex and skip the strip.
Therefore, i'd need a regex to allow 0 to 100 or 0% to 100%. 101 or 101% is invalid.
Presently, i have the following
(?:^((\\%)?100(\\%)?$)|(?:^(\\%)?[0-9]{1,2}(\\%)?)((\\.|,)?[0-9]+)?$)
But that allows 101 but not 101%
Please help! Any help would be greatly appreciated.
You're trying to use regex to do something that regex isn't meant to do. You have the javascript and python tags; you should use regex to ensure that the input is a number, and then use javascript or python to determine whether the number is too big or too small. It will be much easier that way.
Some people, when confronted with a
problem, think “I know, I'll use
regular expressions.” Now they have
two problems.. - Jamie Zawinski
1997
In all seriousness, you are trying to do something that Regular Expressions aren't intended to do, which is perform logic. What you are asking is, does this string of characters contain a number, which is fine and if it does, is it >= 0 && <= 100 which isn't something a regular expression can or should be doing.
While the expression ^(\d{1,2}|100)%?$ will work to tell you if the input matches the pattern, and let you pull the number out using the group. The fact that the result falls into the >=0 && <= 100 range is a side effect. This apparent range checking behavior won't work for any other arbitrary range of numbers. Side effects should be avoided for maintainable code.
Is it the optimal solution? Is the intention obvious from just looking at the expression? I would argue no, not without some comments describing the intent.
JavaScript
I think a better more maintainable solution would be to use the parseInt() function and then explicitly compare the result to >= 0 and <= 100. Explicit is Better than Implicit and is more self documenting.
Python
You will still have to resort to a regular expression to validate the format and extract only the numbers and convert that using int(), testing against the valid range would be redundant but also more explicit. Using the regular expression might not be such a bad option in this case, as long as you comment the intention of the use of the regular expression.
/^(100|[0-9]{1,2})%?$/
either '100' or any number consisting of one or two digits
possibly followed by a percent sign.
Test it here: http://jsbin.com/azeya3.
Oh yes, and the first capture contains the number.
use on javascript "parseInt"
parseInt does following:
"100" -> 100
"10k" -> 10
"100%" -> 100
after this, check if your number is smaller than 101.
example...
function parsePerc(value) {
return parseInt(value) < 101;
}
The question is tagged "python", so I'll answer in Python:
def is0to100(num):
try:
return 0 <= int(num.rstrip('%')) <= 100
except ValueError:
return False
This returns True if the passed-in value, minus the possible trailing '%', is a valid integer between 0 and 100. I didn't benchmark it, but I'd put money that it's way faster than a regexp.
In the spirit of #eykanal's answer, here's a regex:
(\d{1,3})%? works for me in Python with this.
My test text was:
101
100
100%
101%
0%
0
50%
That will get you just the number, which you can then parse and decide whether or not it's in your acceptable range.
First, I agreed with eykanal and Jarrod Roberson
But then, it's so simple with a regex that I doesn't agree more and that it's the better solution according to me:
'(\d\d?|100)(?!\d)\Z' and use of match()
I think that presenting in first position \d\d? for detecting numbers < 100 , and in second position the number 100 alone, is of more natural logic.
Also \d\d? is shorter and more rapidly understood than \d{1,2}
EDIT
better :
'(\d\d?|100)\D?\Z'
But these RE are for detection. Since the aim is to verify, this one is enough:
'(\d\d?|100)\Z'
Match 0 to 100
^100$|^[123456789][0-9]$|^[0-9]$
Match 0% to 100%
^100[%]$|^[123456789][0-9][%]$|^[0-9][%]$