JavaScript regex inline validation for basic calculation string with one operator - javascript

I've written a basic 2 operand calculator app (+ - * /) that uses a couple of inline regex validations to filter away invalid characters as they are typed.
An example looks like:
//check if operator is present
if(/[+\-*\/]/.test(display1.textContent)){
//validate the string each time a new character is added
if(!/^\d+\.?\d*[+\-*\/]?\d*\.?\d*$/.test(display1.textContent)){
console.log('invalid')
return false
}
//validate the string character by character before operator
} else {
if(!/^\d+\.?\d*$/.test(display1.textContent)){
console.log('invalid')
return false
}
}
In the above, a valid character doesn't return false:
23.4x0.00025 (no false returned and hence the string is typed out)
But, if an invalid character is typed the function returns false and the input is filtered away:
23.4x0.(x) x at the end returns a false so is filtered (only one operator allowed per calculation)
23.4x0. is typed
It works pretty well but allows for the following which I would like to deal with:
2.+.1
I would prefer 2.0+0.1
My regex would need an if-then-else conditional stating that if the current character is '.' then the next character must be a number else the next char can be number|.|operator. Or if the current character is [+-*/] then the next character must be a number, else the next char can be any char (while following the overall logic).
The tricky part is that the logic must process the string as it is typed character by character and validate at each addition (and be accurate), not at the end when the string is complete.
if-then-else regex is not supported in JavaScript (which I think would satisfy my needs) so I need to use another approach whilst remaining within the JS domain.
Any suggestions about this specific problem would be really helpful.
Thanks
https://github.com/jdineley/Project-calculator

Thanks #trincot for the tips using capturing groups and look around. This helped me write what I needed:
https://regex101.com/r/khUd8H/1
git hub app is updated and works as desired. Now just need to make it pretty!

For ensuring that an operator is not allowed when the preceding number ended in a point, you can insert a positive look behind in your regex that requires the character before an operator to always be a digit: (?<=\d)
Demo:
const validate = s => /^(\d+(\.\d*)?((?<=\d)[+*/-]|$))*$/.test(s);
document.querySelector("input").addEventListener("input", function () {
this.style.backgroundColor = validate(this.value) ? "" : "orange";
});
Input: <input>

Related

use replace to remove chars not existing in a regex match

I'am trying to allow following pattern for a single html input box with javascript regex
-int (aka any minus number so long it not followed by a zero and is in the first position)
0 (a single zero is allowed)
int (is allowed)
I use this function the remove anything that doesn't match it
$('.dointcheck').live('keyup',
function () {
$(this).val($(this).val().replace((/^((?!:([1-9-]?[0-9])).)/g), ''));
if ($(this).val().length == 0) {
$(this).val(0);
}
});
which doesn't work.
Other examples is:
/[^-0-9]/g it removes any non valid chars but doesnt check if the minus is the beginning and is followed by a zero. It allows minus everywhere in the string
(/^((?!:([1-9-]?[0-9])).)/g Don't allow none.
[^1-9-]?[^0-9]* Allow all...
I think I'am missing something.. Any suggestions would be most appreciated..
You may try this regex
^(0).*|^(-?)([1-9]\d*)?.*|^.*
and replace it with $1$2$3 after input
document.querySelector('input').addEventListener('input', ({ target }) => target.value = target.value.replace(/^(0).*|^(-)?([1-9]\d*)?.*|^.*/g, '$1$2$3'));
<input />
It has three tests:
^(0).* // if it starts with 0, discard everything after it
^(-)?([1-9]\d*)?.* // otherwise it can only starts with a -, or any number that is not a 0. Then followed by other digits, then discard everything after it
^.* // if previous rules are not matched, discard everything
In short:
generally only -, 0-9 are allowed.
if you type a 0 first, nothing will be allowed after.
if you type a 1-9 first, only numbers are allowed after.
if you type a - first, only 1-9 is allowed next, then any digit is allowed after.
I changed your regexp and made it a bit more modular and it worked fine.
function toValidNumber(int) {
return (/^\s*[+-]?(\d+|\d*\.\d+|\d+\.\d*)([Ee][+-]?\d+)?\s*$/).test(int) ? int : 0;
}
$('.dointcheck').live('keyup',
function () {
$(this).val(toValidNumber($(this).val()));
});
Orginal RegEXP in Stackoverflow

Formatting in Javascript

I have a question related to formatting strings.
User should parse a string in the Format XX:XX.
if the string parsed by user is in the format XX:XX i need to return true,
else false:
app.post('/test', (req, res) => {
if (req.body.time is in the format of XX:XX) {
return true
} else {
return false
}
});
You can use the RegExp.test function for this kind of thing.
Here is an example:
var condition = /^[a-zA-Z]{2}:[a-zA-Z]{2}$/.test("XX:XX");
console.log("Condition: ", condition);
The regex that I've used in this case check if the string is composed from two upper or lower case letters fallowed by a colon and other two such letters.
Based on your edits it seems that you're trying to check if a string represents an hour and minute value, if that is the case, a regex like this will be more appropriate /^\d{2}:\d{2}$/. This regex checks if the string is composed of 2 numbers fallowed by a colon and another 2 numbers.
The tool you're looking for is called Regular Expressions.
It is globally supported in almost every development platform, which makes it extremely convenient to use.
I would recommend this website for working out your regular expressions.
/^[a-zA-Z]{2}:[a-zA-Z]{2}&/g is an example of a Regular Expression that will take any pattern of:
[a-zA-Z]{2} - two characters from the sets a-z and A-Z.
Followed by :
Followed by the same first argument. Essentially, validating the pattern XX:XX. Of course, you can manipulate it as to what you want to allow for X.
^ marks the beginning of a string and $ marks the end of it, so ASD:AS would not work even though it contains the described pattern.
try using regex
var str = "12:aa";
var patt = new RegExp("^([a-zA-Z]|[0-9]){2}:([a-zA-Z]|[0-9]){2}$");
var res = patt.test(str);
if(res){ //if true
//do something
}
else{}

Regex (Javascript) - Match certain chat queries

So I'm posting due to me having spent several hours working on a filter that should record only certain chat messages based on the start of said message. I've reached a point where it's about fifty-fifty, but my lack of knowledge regarding regex has stopped me from being able to continue working on it.
Basically, the expression is supposed to match with messages that are one of a few annoying things. My apologies if this gets too specific, I'm unsure of how to get all of the conditions working together.
"word": (any word that is not "notice" or "type: s" - So anything like John:
word_word: (this time, the second word can be anything) - Something like John_Smith:
[Tag]word: or [Tag]word_word: (where a tag is either a unicode character or two characters between square brackets) - Something like [DM]Tom_Cruise: or such
One of the above, minus the colon. This is where I'm having issues. Something like [DM]Tom_Cruise waves.
Starts with (WHISPER) or (SHOUT). It doesn't matter what comes after it, in this case.
I've managed to get a regex that works with most of the situations, but I can't get condition 4 to work without getting unwanted messages.
In addition, if the message (received as a string per line) starts with (OOC), it shouldn't be matched. If it says (OOC) in the message later on, it's alright. If the string ends with "joined the game." or "left the game.", it should also not match.
So... yeah, I'm completely stuck on getting condition 4 to work, and hoped that the community that helped me get this far wouldn't mind answering a (hopefully not too specific) question about it. Here's the expression as I've gotten it:
(?!^\(OOC\))(_[a-z]+:)|(^[a-z]+:)|(^[a-z]+ [a-z]+ )
It can match most of the above conditions, except for 4 and some of 1. I can't figure out how to get the specific words (notice: and type:s) to not match, and 4 is just messing up some of my other conditions. And lastly, it doesn't seem to stop matches if, despite starting with (OOC), the string matches another condition.
Sorry if this is too specific, but I'm completely stuck and basically just picked up regex today. I'll take anything.
EDIT
Examples:
[AT]Smith_Johnson: "Hello there." - matches under Condition 3, works
Tom_Johnson: moves to the side. - matches under Condition 2, works
Notice: That private wooden door is locked. - should not match due to Condition 1, but currently does
Tom hops around like a fool. - Should match under Condition 4, doesn't
(OOC)SmithsonsFriend: hey guys, back - matches, but shouldn't under the not-match specifiers
(WHISPER)Bob_Ross: "Man, this is lame." - Condition 5
West Coast: This is a lovely place to live. - doesn't match due to whitespace, that's good
Joe joined the game. - matches, shouldn't under the not-match specifiers
EDIT TWO
To clarify:
A) string starts with (OOC) - never match
B) string starts with (WHISPER) or (SHOUT) - always match
If neither A nor B apply, then go to conditions 1-4.
You can use this regular expression:
^(?:\(shouts\)|\(whisper\))?(?:\[[A-Z]{1,2}\])?(?!Notice|Note)[A-Za-z]*(?:_[A-Za-z]*)?(?::|\s(?![A-Za-z]*:))(?!(?:joined|left) the game)
^ Start of the string (make sure to check line by line)
(?:\(shouts\)|\(whisper\))? allows optional sequences like (shouts) or (whisper)
(?:\[[A-Z]{1,2}\])? matches a non-capturing group with 1 or 2 A-Z characters inside [] which is optional (because of the ? at the end)
(?!Notice|Note): list of words, which are not part of the subsequent selector
[A-Za-z]* matches as much alphabetical characters as possible
(?:_[A-Za-z]*)? matches a _ followed by alphabetic characters
(?::|\s(?![A-Za-z]*:)) matches a : or a whitespace character \s, which however cannot be followed by [A-Z]:
(?!(?:joined|left) the game) negative lookahead: whole regex does not match, if this pattern matches
You should add the case insensitive flag /i in your regex, if you want to e.g. match (whisper) and (WHISPER).
→ Here are your example texts in an updated regex101 for a live test
Instead of making it one big (HUGE) regular expression, you could make a function that take a message and then check it against a number of regular expression (much flexible and much easier to implement). Like this:
function isValid(msg){
// starts with "WHISPER" or "SHOUT"
if(/^(?:whisper|shout)/i.test(msg)) return true;
// Check if it begins with "notice:" or "type:"
if(/^(?:notice|type)\s*:/i.test(msg)) return false;
// Check if it ends with "joined the game" or "left the game."
if(/(?:joined|left)\s+the\s+game\.?$/i.test(msg)) return false;
// starts with "(ooc)"
if(/^\(ooc\)/i.test(msg)) return false;
// "[at]word:" or "[a]word_word" or "word:" or "word_word" ...
if(/^(?:\[[a-z]{1,2}\])?[a-z_]+:?.*$/i.test(msg)) return true;
return false;
}
Example:
function isValid(msg) {
if (/^(?:whisper|shout)/i.test(msg)) return true;
if (/^(?:notice|type)\s*:/i.test(msg)) return false;
if (/(?:joined|left)\s+the\s+game\.?$/i.test(msg)) return false;
if (/^\(ooc\)/i.test(msg)) return false;
if (/^(?:\[[a-z]{1,2}\])?[a-z_]+:?.*$/i.test(msg)) return true;
return false;
}
function check() {
var string = prompt("Enter a message: ");
if(isValid(string))
alert(string + " is valid!");
else
alert(string + " is not valid!");
}
<button onclick="check()">TRY</button>

Combine whitelist and blacklist in javascript regex expression

I am having problems constructing a regex that will allow the full range of UTF-8 characters with the exception of 2 characters: _ and ?
So the whitelist is: ^[\u0000-\uFFFF] and the blacklist is: ^[^_%]
I need to combine these into one expression.
I have tried the following code, but does not work the way I had hoped:
var input = "this%";
var patrn = /[^\u0000-\uFFFF&&[^_%]]/g;
if (input.match(patrn) == "" || input.match(patrn) == null) {
return true;
} else {
return false;
}
input: this%
actual output: true
desired output: false
If I understand correctly, one of these should be enough:
/^[^_%]*$/.test(str);
!/[_%]/.test(str);
Use negative lookahead:
(?!_blacklist_)_whitelist_
In this case:
^(?:(?![_%])[\u0000-\uFFFF])*$
Underscore is \u005F and percent is \u0025. You can simply alter the range to exclude these two characters:
^[\u0000-\u0024\u0026-\u005E\u0060-\uFFFF]
This will be just as fast as the original regex.
But I don't think that you are going to get the result you really want this way. JS can only go up to \uFFFF, anything past that will be two characters technically.
According to here, the following code returns false:
/^.$/.test('💩')
You need to have a different way to see if you have characters outside that range. This answer gives the following code:
String.prototype.getCodePointLength= function() {
return this.length-this.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g).length+1;
};
Simply put, if the number returned by that is not the same as the number returned by .length() you have a surrogate pair (and thus you should return false).
If your input passes that test, you can run it up against another regex to avoid all the characters between \u0000-\uFFFF that you want to avoid.

How to look for a pattern that might be missing some characters, but following a certain order?

I am trying to make a validation for "KQkq" <or> "-", in the first case, any of the letters can be missing (expect all of them, in which case it should be "-"). The order of the characters is also important.
So quick examples of legal examples are:
-
Kkq
q
This is for a Chess FEN validation, I have validated the first two parts using:.
var fen_parts = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
fen_parts = fen_parts.split(" ");
if(!fen_parts[0].replace(/[1-8/pnbrqk]/gi,"").length
&& !fen_parts[1].replace(/[wb]/,"").length
&& !fen_parts[2].replace(/[kq-]/gi,"").length /*not working, allows KKKKKQkq to be valid*/
){
//...
}
But simply using /[kq-]/gi to validate the third part allows too many things to be introduced, here are some quick examples of illegal examples:
KKKKQkq (there is more than one K)
QK (order is incorrect)
You can do
-|K?Q?k?q?
though you will need to do a second test to ensure that the input is not empty. Alternatively, using only regex:
KQ?k?q?|Qk?q?|kq?|q|-
This seems to work for me...
^(-|(K)?((?!\2)Q)?((?!\2\3)k)?((?!\2\3\4)q)?)$
A .match() returns null if the expression did not match. In that case you can use the logical OR to default to an array with an empty-string (a structure similar to the one returned by .match() on a successful match), which will allow you to check the length of the matched expression. The length will be 0 if the expression did not match, or K?Q?k?q? matched the empty string. If the pattern matches, the length will be > 0. in code:
("KQkq".match(/^(?:K?Q?k?q?|-)$/) || [""])[0].length
Because | is "stronger" than you'd expect, it is necessary to wrap your actual expression in a non-capturing group (?:).
Having answered the question, let's have a look at the rest of your code:
if (!fen_parts[0].replace(/[1-8/pnbrqk]/gi,"").length)
is, from the javascript's perspective equivalent to
if (!fen_parts[0].match(/[^1-8/pnbrqk]/gi))
which translates to "false if any character but 1-8/pnbrqk". This notation is not only simpler to read, it also executes faster as there is no unnecessary string mutation (replace) and computation (length) going on.

Categories

Resources