Update: I find that this behavior is reproducible using a simple regex such as /^[0-9]+$/. It is also reproducible using a custom validator function such as
export function textareaPattern(pattern: RegExp): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} | null => {
const values = control.value.split('\n');
let mismatch = false;
values.forEach(val => {
mismatch = !pattern.test(val);
if(mismatch) {
return {textareaPattern: {value: control.value}}
}
});
return null;
}
}
Original post: I have a regex that is intended to validate that a user has entered multiple, valid IP addresses- one per line- in a text area.
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(/([0-9]|[1-2][0-9]|3[0-2]))?$\n?/gim
The regex works on a happy path, but gets weird once both a valid entry and an invalid entry are present.
If I enter all valid values, the form control is valid, as expected. Example:
10.1.121.2
10.2.121.3
10.3.121.4
If I enter all invalid values, the form control is not valid, as expected. Example:
butter
potato
123
If I enter a combination of valid and invalid values, the form control alternates between valid and invalid, depending on the length of the entry. If the entry's length is even, it's invalid, and odd is valid. How odd.
length: 12, Invalid
10.1.121.1
f
length: 13, Valid
10.1.121.1
fo
I'd think the problem lies in the RegEx, but I'll admit I'm far from comfortable deciphering such a behemoth of a RegEx.
The RegEx should match:
192.168.1.1
255.203.232.1
1.0.12.1
The RegEx should not match:
256.1.12.1
1934.1.22.3
201.foobar.201.1
Minimal, Reproducible Example (updated with solution): https://stackblitz.com/edit/angular-v4f35a?file=src%2Fapp%2Fapp.component.ts
So this boiled down to two issues.
The first problem was that the global flag for RegExes causes the JS engine to track a lastIndex property from which the next search will start when myRegex.test() is called again. Since the regex object is the same throughout the validation process, that lastIndex is updated and referenced as you type, and the form re-validates.
The second problem is that without global, the form would be considered valid as long as one line is correct. So my input could be
192.168.foobar
192.168.1.1
hello world!
and the form is valid because of line 2.
My Solution
I've implemented a custom validator for textareas. It accepts my regex (sans g flag) and splits the contents of the textarea on \n. It immediately assumes no mismatches, then for each line, checks that the line matches the RegEx. If not, it mismatches and returns an object containing the contents of the textarea:
import {AbstractControl, ValidatorFn} from '#angular/forms';
export function multilinePattern(pattern: RegExp): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} | null => {
const values = control.value.split('\n');
if (values.length === 1 && values[0] === '') {
return null;
}
let mismatch = false;
values.forEach((val: string) => {
if (!pattern.test(val)) {
mismatch = true;
}
});
return mismatch ? {multilinePattern: {value: control.value}} : null;
};
}
Related
Im attempting to parse some BBCode with regex, but the nested structures are giving me a headache
What I'm trying to parse is the following:
[COLOR="Red"]Red [COLOR="Green"]Green[/COLOR][/COLOR]
I've come up with the following pattern, which I need to deal with the quotation marks around the color attribute, but it only matches the first leading COLOR and the first closing COLOR. Its not matching in a proper nested arrangement
\[COLOR=(\"?)(.*?)(\"?)]([\s\S]*?)\[\/COLOR\]\
Its being done in dart, as follows, but really I believe the problem might be with my regex pattern rather then the dart implementation
text = text.replaceAllMapped(RegExp(r'\[COLOR=(\"?)(.*?)(\"?)]([\s\S]*?)\[\/COLOR\]', caseSensitive: false, multiLine: true), (match) {
return '<font style="color: ${match.group(2)}">${match.group(4)}</font>';
});
Matching braces (of any kind) are not regular. It's known to be a problem which is context free (can be solved by a stack machine or specified by a context free grammar), but not regular (can be solved by a finite state machine or specified by a regular expression).
While the commonly implemented "regular expressions" can do some non-regular things (due to backreferences), this is not one of those things.
In general, I'd recommend using a RegExp to tokenize the input, then build the stack based machine yourself on top.
Here, because it's simple enough, I'd just match the start and end markers and replace them individually, and not try to match the text between.
var re = RegExp(r'\[COLOR="(\w+)"\]|\[/COLOR\]');
text = text.replaceAllMapped(re, (m) {
var color = m[1]; // The color of a start tag, null if not start tag.
return color == null ? "</span>" : "<span style='color:$color'>";
});
If you want to check that the tags are balanced, we're back to having a stack (in this case so simple it's just a counter):
var re = RegExp(r'\[COLOR="(\w+)"\]|\[/COLOR\]');
var nesting = 0;
text = text.replaceAllMapped(re, (m) {
var color = m[1];
if (color == null) {
if (nesting == 0) {
throw ArgumentError.value(text, "text", "Bad nesting");
}
nesting--; // Decrement on close tag.
return "</span>";
}
nesting++; // Increment on open-tag.
return "<span style='color:$color'>";
});
if (nesting != 0) {
throw ArgumentError.value(text, "text", "Bad nesting");
}
I'm trying to evaluate a string against a set list of parameters with RegExp in Flutter. For example, the string must contain at least:
One capital letter
One lowercase letter
One number from 0-9
One special character, such as $ or !
This is basically for a password entry field of an application. I have set things up, firstly using validateStructure as follows:
abstract class PasswordValidator {
bool validateStructure(String value);
}
Then, I have used the RegExp function as follows:
class PasswordValidatorSpecial implements PasswordValidator {
bool validateStructure(String value) {
String pattern =
r'^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[!##\$&*~£]).{8,}$';
RegExp regEx = new RegExp(pattern);
return regEx.hasMatch(value);
}
}
This does work well, in a sense that when I pass a string/password through it, it does tell me if at least one of the criteria is not met. However, what I would like to do is for the output to be more specific, telling me which of those criteria isn't met.
For example, if the password were to have everything but a number (from 0-9) I would want to be able to get the output to specifically say that a number is missing, but everything else is present.
How would I adapt my code to be able to do that? I thought perhaps by using conditional 'if' statement, although I don't know how that would work. Thanks!
That's right, you can use RegExr to check your RegExp, separate each part and use them separately to have a custom error. Also instead of return a bool value, you can return a String value, such as the following function:
String validateStructure(String value) {
String patternUpperCaseCharacters = r'^(?=.*?[A-Z])';
String patternLowerCaseCharacters = r'^(?=.*?[a-z])';
String patternNumbers = r'^(?=.*?[0-9])';
String patternSpecialCharacters = r'^(?=.*?[!##\$&*~£])';
RegExp regEx = new RegExp(patternUpperCaseCharacters);
if (regEx.hasMatch(value)) {
regEx = new RegExp(patternLowerCaseCharacters);
if (regEx.hasMatch(value)) {
return "More errors";
} else {
return "You need at least one lowercase letter";
}
} else {
return "You need at least one capital letter";
}
}
I have string in format:
172.60.136.145,172.60.136.146,172.60.136.147,........
and I need help in validating the IpAddresses in the string using Regex
For specific such as above
172\.60\.136\.[0-9]{1-3}
which nails it specifically to the range of 172.60.136.0 through 999
of course that doesnt cover valid only IPs, that would allow 172.60.136.0 or 172.60.136.999
(0?0?[1-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-4])(\\.(0?0?[0-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-4])){2}(\\.(0?0?[1-9]|0?[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-4]))
Does test that the IP is valid, it allows 1.0.0.1, through 254.254.254.254, wont allow anything ending in .0 etc.
In PHP:
$tmp = explode(',', $ips);
$tmp = array_map('ip2long', $tmp);
if (array_search(false, $tmp, true) !== false) {
// at least one ip is invalid
}
I'm not sure regex is your best option here as ip addresses are only valid if the numbers are 254 or less
I would split the string at the commas and then split each one of those by a period. I would check that there are 4 sections and that each section can be converted to a number which is 254 or less.
something like this untested pseudo code
string[] addresses = myString.Split(",");
foreach(string address in addresses)
{
string[] numbers = address.Split(".");
if (numbers.Length==4)
{
Foreach(string number in numbers)
{
// need to check that the last number is not a zero as well - not shown here
int value = Convert.ToInt32(number);
if (value<0 && value>254)
{
//invalid
}
}
}
else
{
//invalid
}
}
there are probably classes you could use in your language to help with this but you don't say what language it is. You could do this in c#:
string[] addresses = myString.Split(",");
foreach(string address in addresses)
{
IpAddress ipAddress;
if (!IpAddress.TryParse(address, out ipAddress))
{
//invalid
}
}
if you just wanted to invalid ones you could do:
IPAddress ipAddress;
string[] addresses = myString.Split (",");
foreach (string address in addresses.Where (address => !IPAddress.TryParse (address, out ipAddress)))
{
//all elements in this loop will be invalid entries
// remove the '!' to get only the valid ones
}
Try with:
/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
I am work in Flash Builder 4.
Create e-mail validator on Flex.
Have this code
public var s:String="";
public function checkSumbols(_s:String=""):Boolean {
s=_s; //e-mail address (input mail#supermail.com)
var hDog:int=0;
var hPoint:int=0;
//check #
hDog=s.search("#");
trace(hDog) // It's work
if(hDog==-1) {
return false;
} else {
hPoint=s.substr(hDog).search(".");
trace(hPoint); // PANIC this return always 0
if(hPoint==-1){
return false;
}}
}
You could use regex. Since dot (.) has special meaning in regex you need to put 'escape' character before: yourString.search(/\./);
Should work.
HTH
FTQuest
search() accepts a pattern, and . just means "a single character", so it is probably returning the first single character, which would most likely be at index 0.
You could try search("\.")
I try with search(/[.]/) and it worked well in javascript, I think that It would work in the same mode in as3
I have a regex call that I need help with.
I haven't posted my regex, because it is not relevant here.
What I want to be able to do is, during the Replace, I also want to modify the ${test} portion by doing a Html.Encode on the entire text that is effecting the regex.
Basically, wrap the entire text that is within the range of the regex with the bold tag, but also Html.Encode the text inbetween the bold tag.
RegexOptions regexOptions = RegexOptions.Compiled | RegexOptions.IgnoreCase;
text = Regex.Replace(text, regexBold, #"<b>${text}</b>", regexOptions);
There is an incredibly easy way of doing this (in .net). Its called a MatchEvaluator and it lets you do all sorts of cool find and replace. Essentially you just feed the Regex.Replace method the method name of a method that returns a string and takes in a Match object as its only parameter. Do whatever makes sense for your particular match (html encode) and the string you return will replace the entire text of the match in the input string.
Example: Lets say you wanted to find all the places where there are two numbers being added (in text) and you want to replace the expression with the actual number. You can't do that with a strict regex approach, but you can when you throw in a MatchEvaluator it becomes easy.
public void Stuff()
{
string pattern = #"(?<firstNumber>\d+)\s*(?<operator>[*+-/])\s*(?<secondNumber>\d+)";
string input = "something something 123 + 456 blah blah 100 - 55";
string output = Regex.Replace(input, pattern, MatchMath);
//output will be "something something 579 blah blah 45"
}
private static string MatchMath(Match match)
{
try
{
double first = double.Parse(match.Groups["firstNumber"].Value);
double second = double.Parse(match.Groups["secondNumber"].Value);
switch (match.Groups["operator"].Value)
{
case "*":
return (first * second).ToString();
case "+":
return (first + second).ToString();
case "-":
return (first - second).ToString();
case "/":
return (first / second).ToString();
}
}
catch { }
return "NaN";
}
Find out more at http://msdn.microsoft.com/en-us/library/system.text.regularexpressions.matchevaluator.aspx
Don't use Regex.Replace in this case... use..
foreach(Match in Regex.Matches(...))
{
//do your stuff here
}
Heres an implementation of this I've used to pick out special replace strings from content and localize them.
protected string FindAndTranslateIn(string content)
{
return Regex.Replace(content, #"\{\^(.+?);(.+?)?}", new MatchEvaluator(TranslateHandler), RegexOptions.IgnoreCase);
}
public string TranslateHandler(Match m)
{
if (m.Success)
{
string key = m.Groups[1].Value;
key = FindAndTranslateIn(key);
string def = string.Empty;
if (m.Groups.Count > 2)
{
def = m.Groups[2].Value;
if(def.Length > 1)
{
def = FindAndTranslateIn(def);
}
}
if (group == null)
{
return Translate(key, def);
}
else
{
return Translate(key, group, def);
}
}
return string.Empty;
}
From the match evaluator delegate you return everything you want replaced, so where I have returns you would have bold tags and an encode call, mine also supports recursion, so a little over complicated for your needs, but you can just pare down the example for your needs.
This is equivalent to doing an iteration over the collection of matches and doing parts of the replace methods job. It just saves you some code, and you get to use a fancy shmancy delegate.
If you do a Regex.Match, the resulting match objects group at the 0th index, is the subset of the intput that matched the regex.
you can use this to stitch in the bold tags and encode it there.
Can you fill in the code inside {} to add the bold tag, and encode the text?
I'm confused as to how to apply the changes to the entire text block AND replace the section in the text variable at the end.