Validate password using regex - regex

I am writing a regex to validate the password.
Below are the password policies that i want to cover :
Password can only contain numbers,letters and special character .
Minimum length of the password is 10 and maximum length of the password is 32.
Same character should not appear consecutively 10 or more times.
First character can not be special character.
At least 2 character classes are required.(letters, numbers or special characters)
Special characters allowed - !#+,-./:=#_
Regex that will satisfy first 4 conditions except 5th point :
^(?!.*(.)\1{7})[A-Za-z0-9][\w!#+,./:=#-]{7,23}
How i can validate all the policies together in java ?

The best way to do this is not to use a regex.
A subroutine with separate conditions is much easier to read and maintain:
sub is_password_valid {
my ($pw) = #_;
$pw =~ m{[^a-zA-Z0-9!\#+,\-./:=\#_]}
and return 0;
length($pw) >= 10 && length($pw) <= 32
or return 0;
$pw =~ /(.)\1{9}/s
and return 0;
$pw =~ /^[a-zA-Z0-9]/
or return 0;
($pw =~ /[a-zA-Z]/ + $pw =~ /[0-9]/ + $pw =~ /[^a-zA-Z0-9]/) >= 2
or return 0;
return 1;
}
Or alternatively, since this is basically just one big condition:
sub is_password_valid {
my ($pw) = #_;
return
$pw !~ m{[^a-zA-Z0-9!\#+,\-./:=\#_]} &&
length($pw) >= 10 &&
length($pw) <= 32 &&
$pw !~ /(.)\1{9}/s &&
$pw =~ /^[a-zA-Z0-9]/ &&
($pw =~ /[a-zA-Z]/ + $pw =~ /[0-9]/ + $pw =~ /[^a-zA-Z0-9]/) >= 2
;
}
If this isn't a toy validator for a homework exercise, you should change your requirements. It doesn't make sense to "validate" passwords with a regex.
You should instead require a minimum length, have a much higher maximum length (maybe 255 characters or so), and not restrict the character set used.
If you want to protect against weak passwords, check against haveibeenpwned and let a password cracker (e.g. hashcat) have a go at it.

print "Enter your password please: ";
$p=<STDIN>;chomp $p;
if ( $p =~ /(?!^[\s!#+,-./:=#_])(?=^[\w!#+,-./:=#]{10,32}$)(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!#+,-./:=#_])(?!.*(.)\1{9,}).{10,32}/ ) {print "Welcome"; f=1}

Instead of creating a sub returning true for valid password, an opposite sub can instead return zero or more error messages.
The advantage is of course that the error messages could be presented to the user to pinpoint exactly which rules are broken if any.
sub pwerr {
local $_=pop;
my $s='!#+,-./:=#_'; #allowed special chars
grep $_,
/^[a-z\d$s]+$/i ? 0 : "Password must be just nums, letters and special chars $s",
length()>=10 ? 0 : "Minimum length of the password is 10",
length()<=32 ? 0 : "Maximum length of the password is 32",
!/(.)\1{9}/ ? 0 : "Same char 10 or more in a row",
/^[a-zA-Z0-9]/ ? 0 : "First character can not be special character",
1</[a-z]/i+/\d/+/[$s]/ ? 0 : "At least 2 char classes of letters, numbers or special $s";
}
use strict; use warnings; use Test::More tests => 7;
sub _test_string { join("+",map{/^(\S+)/;$1}pwerr(shift()))||undef }
is(_test_string($$_[0]), $$_[1]) for map[split],grep/\w/,split/\n/,q(
1A!~ Password+Minimum
abc Minimum+At
abcd12345-
abcd12345.
-abcd12345 First
abcd4444444444 Same
abcd12345.abcd12345.abcd12345.xyz Maximum
);

Related

Partial match of strings, operator ( =~ )

I have used " =~ " to compare two strings (The length of two strings is the same.) in my script to allow a don't care condition. If a character is "." in a string, that character is ignored to compare. In other words, it is a partial match case.
comp_test.pl :
#!/usr/bin/perl
use strict;
use warnings;
my $a=".0.0..0..1...0..........0...0......010.1..........";
my $b="10.0..0..1...0..........0...0......010.1..........";
my $c=".0.0..0..1...0..........0...0......010.1..........";
if ($a =~ $b) {
print "a and b same\n";
}
if ($a =~ $c) {
print "a and c same\n";
}
Because of don't care condition by ".", the expected result should be both "a and b same" and "a and c same". However, currently, the result is only "a and c same". Please let me know any good operator or changing "." to "x" may help?
This is not a perl version problem. You are doing a regular expression match. The operand on the left of the =~ is the string and the operand on the right is the regex being applied to it.
This can be used for the kind of partial matching you are doing, given that the strings are the same length and each character of the regular expression matches a character of the string, but only where there is a . on the right. Where there is a 1 or a 0 in the regular expression ($b in the case of $a =~ $b), there must be an exactly matching character in the string ($a), not a ..
To do the kind of partial match you seem to want to do, you can use a bitwise exclusive or, like so:
sub partial_match {
my ($string_1, $string_2) = #_;
return 0 if length($string_1) != length($string_2);
# bitwise exclusive or the two strings together; where there is
# a 0 in one string and a 1 in the other, the result will be "\1".
# count \1's to get the number of mismatches
my $mismatches = ( $string_1 ^ $string_2 ) =~ y/\1//;
return $mismatches == 0;
}
While . matches 1 (or any other character), 1 doesn't match . (or any other character other than 1).
The following is a fast solution. It performs best when most strings match (since it always checks the entire string).
sub is_match { ( ( $_[0] ^ $_[1] ) =~ tr/\x00\x1E\x1F//c ) == 0 }
say is_match($a, $b) ? "match" : "no match";
say is_match($b, $c) ? "match" : "no match";
How it works:
Hex of characters
=================
30 30 31 31 2E 2E "0011.."
30 31 30 31 30 31 "010101"
XOR -----------------
00 01 01 00 1E 1F
^^ ^^ 2 mismatches
This solution even works if one of the strings is shorter than the other (since the XOR will result in 30, 31 or 2E for the extra characters).
The following is a fast solution. It performs best when most strings don't match (since it stops checking as soon as a match is impossible).
sub make_matcher {
my $pat =
join '',
map { $_ eq '.' ? $_ : "[.\Q$_\E]" }
split //, $_[0];
return qr/^$pat\z/;
}
sub is_match { $_[0] =~ make_matcher($_[1]) }
say is_match($a, $b) ? "match" : "no match";
say is_match($b, $c) ? "match" : "no match";
Tested.
From my understanding, you are trying to compare the length of 2 strings.Basically, only the length of the sting need to be compared, not the bit-wise characters.
my $a=".0.0..0..1...0..........0...0......010.1..........";
my $b="10.0..0..1...0..........0...0......010.1..........";
my $c=".0.0..0..1...0..........0...0......010.1..........";
So the code could be:
if(length($a) == length($b))
{
print "match found";
}
else
{
print "No match";
}

Matching all characters in a string except one in any position

How to match (preferably in perl) all strings that match the query string except one character?
Query: TLAQLLLDK
Want to match: xLAQLLLDK, TxAQLLLDK, TLxQLLLDK, etc.
Where 'x' is any capital letter '[A-Z]'.
Use alternation operator.
^(?:[A-Z]LAQLLLDK|T[A-Z]AQLLLDK|TL[A-Z]QLLLDK|.....)$
Likewise fill all..
You can do that by writing a terrible regular expression, which will be horribly slow to build and even slower to execute, or you can just don't use regexes for things like these and write a function that just compares both strings character after character, allows for one "mistake" and returns True only if there was exactly one mistake.
How to match (preferably in perl) all strings that match the query string except one character?
Expanding the answer of #Avinash, by generating the required regular expression dynamically at run time:
my $query = 'TLAQLLLDK';
my $re_proto = '(' . join( '|', map { (my$x=$query)=~s/^(.{$_})./$1\[A-Za-z]/; $x; } (0 .. length($query)-1) ) . ')';
my $re = qr/^$re_proto$/;
my #input = qw(xLAQLLLDK TxAQLLLDK TLxQLLLDK);
my #matches = grep { /$re/ } #input;
print "#matches\n";
(I had to include the [a-z] too, since your example input uses the x as the marker.)
If you need to do that very often, I would advise to cache the generated regular expressions.
Is this what you are looking for?
#!/usr/bin/perl
use strict;
my #str = ("ULAQLLLDK","TAAQLLLDK","TLCQLLLDK","TLAQLLLDK");
while(<#str>){
if (/[A-S,U-Z]LAQLLLDK|T[A-K,M-Z]AQLLLDK|TL[B-Z]QLLLDK/ ){
print "$_\n";
}
}
output:
ULAQLLLDK
TAAQLLLDK
TLCQLLLDK
There are only 9 x 25 = 225 such strings, so you may as well generate them all and put them in a hash for comparison
use strict;
use warnings;
use 5.010;
my %matches;
my $s = 'TLAQLLLDK';
for my $i (0 .. length($s) - 1) {
my $c = substr $s, $i, 1;
for my $cc ('A' .. 'Z') {
substr(my $ss = $s, $i, 1) = $cc;
++$matches{$ss} unless $cc eq $c;
}
}
printf "%d matches found\n", scalar keys %matches;
for ( qw/ TLAQLLLDK TLAQLXLDK / ) {
printf "\$matches{%s} = %s\n", $_, $matches{$_} // 'undef';
}
output
225 matches found
$matches{TLAQLLLDK} = undef
$matches{TLAQLXLDK} = 1

Pattern matching in perl (Lookahead and Condition on word Index)

I have a long string, containing alphabetic words and each delimited by one single character ";" . The whole string also starts and ends with a ";" .
How do I count the number of occurrences of a pattern (started with ";") if index of a success match is divisible by 5.
Example:
$String = ";the;fox;jumped;over;the;dog;the;duck;and;the;frog;"
$Pattern = ";the(?=;f)"
OUTPUT: 1
Since:
Note 1: In above case, the $Pattern ;the(?=;f) exists as the 1st and 10th words in the $String; however; the output result would be 1, since only the index of second match (10) is divisible by 5.
Note 2: Every word delimited by ";" counts toward the index set.
Index of the = 1 -> this does not match since 1 is not divisible by 5
Index of fox = 2
Index of jumped = 3
Index of over = 4
Index of the = 5 -> this does not match since the next word (dog) starts with "d" not "f"
Index of dog = 6
Index of the = 7 -> this does not match since 7 is not divisible by 5
Index of duck = 8
Index of and = 9
Index of the = 10 -> this does match since 10 is divisible by 5 and the next word (frog) starts with "f"
Index of frog = 11
If possible, I am wondering if there is a way to do this with a single pattern matching without using list or array as the $String is extremely long.
Use Backtracking control verbs to process the string 5 words at a time
One solution is to add a boundary condition that the pattern is preceded by 4 other words.
Then setup an alteration so that if your pattern is not matched, the 5th word is gobbled and then skipped using backtracking control verbs.
The following demonstrates:
#!/usr/bin/env perl
use strict;
use warnings;
my $string = ";the;fox;jumped;over;the;dog;the;duck;and;the;frog;";
my $pattern = qr{;the(?=;f)};
my #matches = $string =~ m{
(?: ;[^;]* ){4} # Preceded by 4 words
(
$pattern # Match Pattern
|
;(*SKIP)(*FAIL) # Or consume 5th word and skip to next part of string.
)
}xg;
print "Number of Matches = " . #matches . "\n";
Outputs:
Number of Matches = 1
Live Demo
Supplemental Example using Numbers 1 through 100 in words
For additional testing, the following constructs a string of all numbers in word format from 1 to 100 using Lingua::EN::Numbers.
For the pattern it looks for a number that's a single word with the next number that begins with the letter S.
use Lingua::EN::Numbers qw(num2en);
my $string = ';' . join( ';', map { num2en($_) } ( 1 .. 100 ) ) . ';';
my $pattern = qr{;\w+(?=;s)};
my #matches = $string =~ m{(?:;[^;]*){4}($pattern|;(*SKIP)(*FAIL))}g;
print "#matches\n";
Outputs:
;five ;fifteen ;sixty ;seventy
Reference for more techniques
The following question from last month is a very similar problem. However, I provided 5 different solutions in addition to the one demonstrated here:
In Perl, how to count the number of occurences of successful matches based on a condition on their absolute positions
You can count the number of semicolons in each substring up to the matching position. For a million-word string, it takes 150 seconds.
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
my $string = join ';', q(),
map { qw( the fox jumped over the dog the duck and the frog)[int rand 11] }
1 .. 1000;
$string .= ';';
my $pattern = qr/;the(?=;f)/;
while ($string =~ /$pattern/g) {
my $count = substr($string, 0, pos $string) =~ tr/;//;
say $count if 0 == $count % 5;
}
Revised Answer
One relatively simple way to achieve what you want is by replacing the delimiters in the original text that occur on a 5-word-index boundary:
$text =~ s/;/state $idx++ % 5 ? ',' : ';'/eg;
Now you just need to trivially adjust your $pattern to look for ;the,f instead of ;the;f. You can use the =()= pseudo-operator to return the count:
my $count =()= $text =~ /;the(?=,f)/g;
Original answer after the break. (Thanks to #choroba for pointing out the correct interpretation of the question.)
Character-Based Answer
This uses the /g regex modifier in combination with pos() to look at matching words. For illustration, I print out all matches (not just those on 5-character boundaries), but I print (match) beside those on 5-char boundaries. The output is:
;the;fox;jumped;over;the;dog;the;duck;and;the;frog
^....^....^....^....^....^....^....^....^....^....
`the' #0 (match)
`the' #41
And the code is:
#!/usr/bin/env perl
use 5.010;
my $text = ';the;fox;jumped;over;the;dog;the;duck;and;the;frog';
say $text;
say '^....^....' x 5;
my $pat = qr/;(the)(?=;f)/;
#$pat = qr/;([^;]+)/;
while ($text =~ /$pat/g) {
my $pos = pos($text) - length($1) - 1;
say "`$1' \#$pos". ($pos % 5 ? '' : ' (match)');
}
First of, pos is also possible as a left hand side expression. You could make use of the \G assertion in combination with index (since speed is of concern for you). I expanded your example to showcase that it only "matches" for divisibles of 5 (your example also allowed for indices not divisible by 5 to be 1 a solution, too). Since you only wanted the number of matches, I only used a $count variable and incremented. If you want something more, use the normal if {} clause and do something in the block.
my $string = ";the;fox;jumped;over;the;dog;the;duck;and;the;frog;or;the;fish";
my $pattern = qr/;the(?=;f)/;
my ($index,$count, $position) = (0,0,0);
while(0 <= ($position = index $string, ';',$position)){
pos $string = $position++; #add one to $position, to terminate the loop
++$count if (!(++$index % 5) and $string =~/\G$pattern/);
}
say $count; # says 1, not 2
You could use the experimental features of regexes to solve you problem (especially the (?{}) blocks). Before you do, you really should read the corresponding section in the perldocs.
my ($index, $count) = (0,0);
while ($string =~ /; # the `;'
(?(?{not ++$index % 5}) # if with a code condition
the(?=;f) # almost your pattern, but we'll have to count
|(*FAIL)) # else fail
/gx) {
$count++;
}

shorten preg_match(or) code

I was trying to make a program to match the string which must contains number of 0-9 using regex.
This was correct but it somehow seems long. Do anyone has alternatives for this code?
if($str = (preg_match('/[1]/', $str) && preg_match('/[2]/', $str)
&& preg_match('/[3]/', $str) && preg_match('/[4]/', $str)
&& preg_match('/[5]/', $str) && preg_match('/[6]/', $str)
&& preg_match('/[7]/', $str) && preg_match('/[8]/', $str)
&& preg_match('/[9]/', $str) && preg_match('/[0]/', $str))) {
//do something
}
Simply use a character range: [0-9].
if (preg_match('/[0-9]/', $str)) {
echo 'It does.';
} else {
echo 'It doesn\'t.';
}
If you were ever in a situation where you wouldn't want "6" you could even change it to [012345789] if you really want to.
As Floris mentions, your code is pretty confusing - if you want all the characters to be displayed individually at least once, you can simply use strpos with a loop:
<?php
$match = true;
for ($i = 0; $i < 9; $i++) {
if (strpos($string, (string)$i) === false) {
$match = false;
break; //No need to continue the loop - we already got our answer
}
}
if ($match) {
echo 'Yes!';
} else {
echo 'No!';
}
?>
Alternatively, I apparently already gave you a function to do this?
Looks like you have all the conditions ANDed together. In that following lookahead based regex should work for you:
preg_match('/(?=[^0]*0)(?=[^1]*1)(?=[^2]*2)(?=[^3]*3)(?=[^4]*4)(?=[^5]*5)(?=[^6]*6)(?=[^7]*7)(?=[^8]*8)(?=[^9]*9)./', $str)
If you want to make sure that your string contains all the digits 0-9, you should probably strip anything that is not a digit, then take unique characters only, and make sure the string length is 10. This is more compact than your expression but not necessarily faster. The php function count_chars does much of this work (using mode = 3):
$str = "12345abcde789d9999969";
preg_match_all('/\d+/', $str, $matches);
$distinct = strlen(count_chars(join($matches[0]),3));
if($distinct==10)
{
echo "all ten digits are present<br>";
}
else
{
echo "not all digits are present<br>";
}
echo "there are " . $distinct . " distinct digits<br>";
Output of the above:
not all digits are present
there are 9 distinct digits

Perl pattern matching with pattern arithmetic

I want to understand how can I do arithmetic on matched sub-patterns in perl regex.
This is just a sample code and I want to understand how can I use \1 (already matched sub-pattern. in this case - 7) to match pattern+1 (8)
my $y = 77668;
if($y =~ /(\d)\1(\d)\2\1+1/) #How to increment a previously
#matched sub-pattern and form a pattern?
{
print $y;
}
EDIT
From the answers, I see that pattern arithmetic is not possible.
This is what I want to achieve.
I want to form a regex which will match this pattern:
N-3N-2N-1NNN+1N+2N+3 (N = 3,4,5,6
Its possible via regex code blocks:
my $y = 77668;
if($y =~ /(\d)\1(\d)\2(??{$1+1})/ ) {
print $y;
}
In this snippet (??{ CODE }) returns another regex that must match, so this regex looks like "8" ($1+1). As a result, whole regex will match only if 5th digit is greather and 1st by 1. But drawback with 1st digit is 9, this code block will return "10", so possible its wrong behavior, but you said nothing about what must be done in this case.
Now about N-3N-2N-1NNN+1N+2N+3 question, you can match it with this regex:
my $n = 5;
if( $y =~ /(??{ ($n-3).($n-2).($n-1).$n.($n+1).($n+2).($n+3) })/ ){
Or more "scalable" way:
my $n = 5;
if( $y =~ /(??{ $s=''; $s .= $n+$_ foreach(-3..3); $s; })/ ){
Again, what we must do if $n == 2 ?? $n-3 will be -1. Its not a simply digit cus it have sign, so you should think about this cases.
One another way. Match what we have and then check it.
if( $y =~ /(\d)(\d)(\d)(\d)(\d)(\d)(\d)/ ) {
if( $1 == ($4-3) && $2 == ($4-2) && $3 == ($4-1) && $6 == ($4+1) && $7 == ($4+2) && $7 == ($4+3) ) {
#...
Seems this method litle bit clumsy, but its obivious to everyone (i hope).
Also, you can optimize your regex since 7 ascending digits streak is not so frequent combination, plus get some lulz from co-workers xD:
sub check_number {
my $i;
for($i=1; $i<length($^N); $i++) {
last if substr($^N, $i, 1)<=substr($^N, $i-1, 1);
}
return $i<length($^N) ? "(*FAIL)" : "(*ACCEPT)";
}
if( $y =~ /[0123][1234][2345][3456][4567][5678][6789](??{ check_number() })/ ) {
Or... Maybe most human-friendly method:
if( $y =~ /0123456|1234567|2345678|3456789/ ) {
Seems last variant is bingo xD Its good example about not searching regex when things are so simple)
Of course this is possible. We are talking about Perl regexes after all. But it will be rather ugly:
say "55336"=~m{(\d)\1(\d)\2(\d)(?(?{$1+1==$3})|(*F))}?"match":"fail";
or pretty-printed:
say "55336" =~ m{ (\d)\1 (\d)\2 (\d)
(? (?{$1+1==$3}) # true-branch: nothing
|(*FAIL)
)
}x
? "match" : "fail";
What does this do? We collect the digits in ordinary captures. At the end, we use an if-else pattern:
(? (CONDITION) TRUE | FALSE )
We can embed code into a regex with (?{ code }). The return value of this code can be used as a condition. The (*FAIL) (short: (*F)) verb causes the match to fail. Use (*PRUNE) if you only want a branch, not the whole pattern to fail.
Embedded code is also great for debugging. However, older perls cannot use regexes inside this regex code :-(
So we can match lots of stuff and test it for validity inside the pattern itself. However, it might be a better idea to do that outside of the pattern like:
"string" =~ /regex/ and (conditions)
Now to your main pattern N-3N-2N-1NNN+1N+2N+3 (I hope I parsed it correctly):
my $super_regex = qr{
# N -3 N-2 N-1 N N N+1 N+2 N+3
(\d)-3\1-2\1-1\1\1(\d)(\d)(\d)
(?(?{$1==$2-1 and $1==$3-2 and $1==$4-3})|(*F))
}x;
say "4-34-24-144567" =~ $super_regex ? "match" : "fail";
Or did you mean
my $super_regex = qr{
#N-3 N-2 N-1 N N N+1 N+2 N+3
(\d)(\d)(\d) (\d)\4 (\d)(\d)(\d)
(? (?{$1==$4-3 and $2==$4-2 and $3==$4-1 and
$5==$4+1 and $6==$4+2 and $7==$4+3})|(*F))
}x;
say "123445678" =~ $super_regex ? "match" : "fail";
The scary thing is that these even works (with perl 5.12).
We could also generate parts of the pattern at match-time with the (??{ code }) construct — the return value of this code is used as a pattern:
my $super_regex = qr{(\d)(??{$1+1})(??{$1+2})}x;
say "234"=~$super_regex ? "match":"fail"
et cetera. However, I think readability suffers more this way.
If you need more than nine captures, you can use named captures with the
(?<named>pattern) ... \k<named>
constructs. The contents are also available in the %+ hash, see perlvar for that.
To dive further into the secrets of Perl regexes, I recommend reading perlre a few times.