3 out of 4 conditions in regex java [duplicate] - java

This question already has answers here:
The best way to match at least three out of four regex requirements
(2 answers)
Closed 2 years ago.
I am trying to create a java to check strength of a password with regex. The password must pass 3 out of 4 conditions:
lowercase
uppercase
contains digits
has special characters
The code looks like below:
import java.util.*;
public class StringPasswordStrength {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("Enter password: ");
String password = input.nextLine();
boolean test = checkStrength(password);
if (test) {
System.out.print("OK");
}
else {
System.out.print("Not strong enough!");
}
}
public static boolean checkStrength(String password) {
if (password.matches("^(?=.*[a-zA-Z][0-9][!##$%^&*])(?=.{8,})")){
return true;
}
else {
return false;
}
}
}
However when the password is Passw0rd it doesn't accept. How can I change the conditions in regex that the program would accept Passw0rd because it passes 3 out of 4 conditions: uppercase, lowercase and digit?

I would suggest avoiding a potentially cryptic regular expression for this, and instead to provide something easier to read, understand and maintain (albeit more verbose).
Something like this (depending on what your conditions are, of course). For example, a length test should be mandatory:
public boolean isValid(String password) {
// password must contain 3 out of 4 of lowercase, uppercase, digits,
// and others (punctuation, symbols, spaces, etc.).
if (password == null) {
return false;
}
if (password.length() < 8) {
return false;
}
char[] chars = password.toCharArray();
int lowers = 0;
int uppers = 0;
int digits = 0;
int others = 0;
for (Character c : chars) {
if (Character.isLowerCase(c)) {
lowers = 1;
} else if (Character.isUpperCase(c)) {
uppers = 1;
} else if (Character.isDigit(c)) {
digits = 1;
} else {
others = 1;
}
}
// at least 3 out of 4 tests must pass:
return (lowers + uppers + digits + others >= 3);
}
I understand this is not a direct answer to your question, and so may not meet your needs.
I am also deliberately avoiding the discussion about char[] vs. String for password handling in Java - for example, see this post.
EDIT: Removed wording relating to password length, and changed related code to reflect the question.

You could define a set of rules (regex), count how many a given password comply with and compare with the minimum you require. A possible implementation could be:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
/**
* Patterns to be tested in your passwords. If you want some of them
* mandatory, you can define them in a "mandatoryPatterns" list and
* check those ones always.
*/
static List<String> patterns = Arrays.asList(
".*[A-Z]+.*",
".*[a-z]+.*"
);
/** Number of required patterns. */
static long requiredPatterns = 1;
/** This functions counts the number of patterns that a password matches. */
static long passwordStrength(String password) {
return patterns.stream().filter(password::matches).count();
}
static boolean checkStrength(String password) {
return passwordStrength(password) >= requiredPatterns;
}
Stream.of("", "foo", "BAR", "FooBar").forEach(pass -> {
System.out.println(pass);
System.out.println(passwordStrength(pass));
System.out.println(checkStrength(pass));
});

Your issue has been pointed out by another user, along with a solution. This is an alternative solution.
Have 4 Pattern objects, one for each requirement
Pattern uppercase = Pattern.compile("[A-Z]");
Pattern number = Pattern.compile("\\d+");
Pattern symbol = Pattern.compile("[+&$%!#]");
Pattern other = ...;
String#matches "compiles" the regex every time it is called, which can be time consuming. By using Pattern objects, you'll be using already-compiled regex patterns.
Add the requirements to a list
List<Pattern> requirements = Arrays.asList(uppercase, number, symbol, other);
Loop over the list of requirements. For each requirement, check if the password matches the requirement. If the element does, increase a counter which tracks how many requirements have already been met.
If the requirements equals 3 (or is greater than 3), return true. If the loop exits gracefully, that means 3 requirements were not met; return false if the loop exits gracefully.
public boolean isStrong(String password) {
int requirementsMet = 0;
for(Pattern req : requirements) {
if(req.matcher(password).matches())
requirementsMet++;
if(requirementsMet >= 3)
return true;
}
return false;
}

I assume the four requirements, of which at three must be met, are as follows. The string must contain:
a letter
a digit
a character in the string "!##$%^&*"
at least 8 characters
Is the use of a regular expression the best way to determine if a password meets three of the four requirements? That may be a valid question but it's not the one being asked or the one that I will attempt to answer. The OP may just be curious: can this problem be solved using a regular expression? Moreover, even if there are better ways to address the problem there is educational value in answers to the specific question that's been posed.
I am not familiar with Java, but I can suggest a regular expression that uses Ruby syntax. Readers unfamiliar with Ruby should be able to understand the expression, and its translation to Java should be straightforward. (If a reader can perform that translation, I would be grateful to see an edit to this answer that provides the Java equivalent at the end.)
r = /
((?=.*[a-z])) # match a lowercase letter in the string in
# a positive lookahead in cap grp 1, 0 times
((?=.*\d)) # match a digit in the string in a positive
# lookahead in cap grp 2, 0 times
((?=.*[!##$%^&*])) # match a special character in in the string
# in a positive lookahead in cap grp 3, 0 times
(.{8,}) # match at least 8 characters in cap grp 4, 0 times
\g<1>\g<2>\g<3> # match conditions 1, 2 and 3
| # or
\g<1>\g<2>\g<4> # match conditions 1, 2 and 4
| # or
\g<1>\g<3>\g<4> # match conditions 1, 3 and 4
| # or
\g<2>\g<3>\g<4> # match conditions 2, 3 and 4
/xi # case indiff & free-spacing regex def modes
\g{2}, for example, is replaced by the sub-expression contained in capture group 2 ((?=.*\d)). The first four lines each contain an expression in a capture group, with the capture group repeated zero times. This is just a device to define the subexpressions in the capture groups for retrieval later.
Let's test some strings.
"Passw0rd".match? r #=> true (a letter, digit and >= 8)
"ab2c#45d".match? r #=> true (all 4 conditions satisfied)
"ab2c#5d".match? r #=> true (a letter, digit and special char)
"ab2c345d".match? r #=> true (a letter, digit and >= 8)
"ab#c?def".match? r #=> true (a letter, special char and >= 8)
"21#6?512".match? r #=> true (a digit, special char and >= 8)
"ab26c4".match? r #=> false (only letter and digit)
"a$b#c".match? r #=> false (only letter and special char)
"abc ef h".match? r #=> false (only letter and >= 8)
"12 45 78".match? r #=> false (only digit and >=8)
"########".match? r #=> false (only special char and >= 8)
"".match r #=> false (no condition matched)
To use named capture groups, ((?=.*[a-z])) would be replaced with, say,
(?<letter>(?=.*[a-z]))
and \g<1>\g<2>\g<3> would be replaced by something like
\g<letter>\g<digit>\g<spec_char>

To answer your question, the sequence in which you have provided
1st: [a-zA-Z] characters
2nd: [0-9] Numbers
3rd: [!##$%^&*] Sp. Chars
The occurrence of literals in this sequence is must.
Abcd1234# will pass but Abcd1234#A will not pass, as A appears again after [!##$%^&*]
And a positive lookahead must include this sequence only. If you provied any special char before, it will not be validated, similarly in your case, characters after number is not expected.
use a positive lookahead for each combination, use groups for each
Try this instead(This is my work after several lookups):
^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!##$%&*_])(?!.*[`~^=+/?<>():;-])(?=\S+$).{8,20}$
In this case: any of the provided chars or literals can appear anywhere.
(?=.*[0-9])
(?=.*[a-z])
(?=.*[A-Z])
(?=.*[!##$%&*_])
(?!.*[`~^=+/?<>():;-])
(?=\S+$)

Related

validate a user input

Hello I'm new to programming and I'm having a trouble understanding my assignment. I know that this might be a really simple problem for you guys and I'm sorry for that. Is it possible that she's just asking me to write a method that will perform the given instructions?
Write a program to find if the user input is valid base on the instructions.**
a string must have at least nine characters
a string consists of letters and numbers only.
a string must contain at least two digits.
You can simply use the regex, ^(?=(?:\D*\d){2})[a-zA-Z\d]{9,}$ which can be explained as follows:
^ : asserts position at start of a line
Positive Lookahead (?=(?:\D*\d){2})
Non-capturing group (?:\D*\d){2}
{2} matches the previous token exactly 2 times
\D matches any character that's not a digit (equivalent to [^0-9])
* matches the previous token between zero or more time (greedy)
\d matches a digit (equivalent to [0-9])
The pattern, [a-zA-Z\d]{9,} :
{9,} matches the previous token between 9+ times (greedy)
a-z matches a single character in a-z
A-Z matches a single character in A-Z
\d matches a digit (equivalent to [0-9])
$ : asserts position at the end of a line
Demo:
import java.util.stream.Stream;
public class Main {
public static void main(String args[]) {
//Test
Stream.of(
"helloworld",
"hello",
"hello12world",
"12helloworld",
"helloworld12",
"123456789",
"hello1234",
"1234hello",
"12345hello",
"hello12345"
).forEach(s -> System.out.println(s + " => " + isValid(s)));
}
static boolean isValid(String s) {
return s.matches("^(?=(?:\\D*\\d){2})[a-zA-Z\\d]{9,}$");
}
}
Output:
helloworld => false
hello => false
hello12world => true
12helloworld => true
helloworld12 => true
123456789 => true
hello1234 => true
1234hello => true
12345hello => true
hello12345 => true
Requirement #1: a string must have at least nine characters
This is solved by checking whether the length of the String is greater than 9, with s.length()>9
Requirement #2: a string consists of letters and numbers (whole numbers) only.
Use the regex [a-zA-Z0-9]+, which matches all Latin alphabet characters and numbers.
Requirement #3: a string must contain at least two digits.
I've written a method that loops through every character and uses Character.isDigit() to check whether it is a digit.
Check it out:
public static boolean verify(String s) {
final String regex = "[a-zA-Z0-9]+";
System.out.println(numOfDigits(s));
return s.length() > 9 && s.matches(regex) && numOfDigits(s) > 2;
}
public static int numOfDigits(String s) {
int a = 0;
int b = s.length();
for (int i = 0; i < b; i++) {
a += (Character.isDigit(s.charAt(i)) ? 1 : 0);
}
return a;
}

Regular Expression Matching for number only with 2 digits repeated

I am trying to match number which are having 2 digits and those are repeated and the length of number is 7 digits .
I want to match these numbers from java .
example numbers:
3433434
6776767
9000999
Please help to create the regular expression for these pattern numbers
I'd recommend hiding any regexes inside helper methods:
private static boolean matchesCriteria(String s) {
return exactlySevenDigits(s) && containsRepeatedDigits(s);
}
private static boolean exactlySevenDigits(String s) {
return s.matches("\\d{7}");
}
private static boolean containsRepeatedDigits(String s) {
return s.matches(".*(\\d)\\1.*");
}
Example results:
3433434 true
6776767 true
9000999 true
1234567 false (no repeating numbers)
12331233 false (too long)
123356A false (not all digits)
You can do it as follows:
String str = "3433434";
boolean sevenOf2Digits = str.length() == 7 &&
str.matches("(\\d)\\1*+(\\d)(\\1|\\2)*");
System.out.println(sevenOf2Digits);
The first (\\d) captures the first digit in group 1.
\\1 is a backreference to group 1, so the first digit. * is 0 or more of those digits, + makes that possessive, which is required to prevent the next (\\d) from matching the same digit.
The following (\\d) captures the second digit in group 2.
(\\1|\\2)* just matches 0 or more of any combination of the first or second digits.
I separated out the length check for simplicity. If you want a pure regex solution, you can add the length check to your regex in the form of a lookahead by adding (?=.{7}$) to the start of your regex.
"(?=.{7}$)(\\d)\\1*+(\\d)(\\1|\\2)*"
With regex it is a little complicated, I would use this way (Java 8+) instead :
boolean check = myString.chars()
.mapToObj(i -> (char) i)
.collect(Collectors.toSet())
.size() == 2;
The idea is to create a Set with the character of this string, if the size of the Set is 2 then it is correct String else it is not.
Or as Ralf Renz mention in comment, you can use this short way :
boolean check = myString.chars().distinct().count() == 2;
So your final solution should look like this :
boolean check = myString.matches("\\d{7}") && myString.chars().distinct().count() == 2;
(?=^.{7}$)(\d)\1*(?!\1)(\d)(?:\1|\2)*
This should do it. It finds a digit and repeats, then finds a second digit and repeats. Then it checks if the rest of the number is one of those 2.
I'll explain in detail what this does.
(?=^.{7}$): Before starting, make sure there are 7 characters between the start and end. If shorter or longer, fast fails.
(\d)\1*(?!\1)(\d): Get the first digit and save it in a capture group. Then matches if the captured digit is also the next one. If there is only a single digit, the next part will catch that. Last digit should always be different then the first one.
(?:\1|\2): repeat the 2 captured digits 0 or more times.
String regex = "[10]{7}|[20]{7}|[21]{7}|[30]{7}|[31]{7}|[32]{7}|[40]{7}|[41]{7}|[42]{7}|[43]{7}|[50]{7}|[51]{7}|[52]{7}|[53]{7}|[54]{7}|[60]{7}|[61]{7}|[62]{7}|[63]{7}|[64]{7}|[65]{7}|[70]{7}|[71]{7}|[72]{7}|[73]{7}|[74]{7}|[75]{7}|[76]{7}|[80]{7}|[81]{7}|[82]{7}|[83]{7}|[84]{7}|[85]{7}|[86]{7}|[87]{7}|[90]{7}|[91]{7}|[92]{7}|[93]{7}|[94]{7}|[95]{7}|[96]{7}|[97]{7}|[98]{7}";
System.out.println(Pattern.matches(regex, "3433434"));
System.out.println(Pattern.matches(regex, "6776767"));
System.out.println(Pattern.matches(regex, "9000999"));
That should do it. All combinations of two digits with a length of 7.

How can I test if a string contains any uppercase letter using Java with regular expression? [duplicate]

This question already has answers here:
How do I check if a Java String contains at least one capital letter, lowercase letter, and number?
(9 answers)
Closed 5 years ago.
I want to use only regular expression
Raida => true
raida => false
raiDa => true
I have tried this :
String string = "Raida"
boolean bool = string.contains("?=.*[A-Z]");
System.out.println(bool);
but it is not working
A simple solution would be:
boolean hasUpperCase = !string.equals(string.toLowerCase());
You convert the String to lowercase, if it is equal to the original string then it does not contain any uppercase letter.
In your example Raida you'll be compairing
Raida to raida these two are not equal so meaning the original string contains an uppercase letter
The answer with regular expression solution has already been posted as well as many other rather convenient options. What I would also suggest here is using Java 8 API for that purpose. It might not be the best option in terms of performance, but it simplifies code a lot. The solution can be written within one line:
string.chars().anyMatch(Character::isUpperCase);
The benefit of this solution is readability. The intention is clear. Even if you want to inverse it.
Something which is close to your original idea. You basically just check whether there is a part in the string which contains an upper case letter - there can be any other characters after and before it. Here I included a small main method for testing purposes.
public static void main(String[] args) {
test("raid");
test("raId");
test("Raida");
test("R");
test("r");
test(".");
test("");
}
public static void test(String word) {
//(?s) enables the DOTALL mode
System.out.println(word + " -> " + word.matches("(?s).*[A-Z].*"));
}
I edited the example to deal with line breaks too. I just tested in on a Windows machine. This now uses DOTALL: https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html#DOTALL. In this mode, the expression . matches any character, including a line terminator.
Your pattern just needs to be surrounded by ().
Pattern pattern = Pattern.compile("(?=.*[A-Z])");
Matcher matcher = pattern.matcher(string);
while (matcher.find()) {
bool = true;
}
If you want an alternative to regex, try using Character.isUpperCase().
boolean bool = false;
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
if (Character.isUpperCase(c)) {
bool = true;
break;
}
}
System.out.println(bool);
You can replace all non UpperLetter and calculate the length, if it is great or equal to 1 this mean there are at least one upper letter :
String input = "raiDa";
boolean check = input.replaceAll("[^A-Z]", "").length() >= 1;
Beside String::contain not use regex read the documentation
Ideone demo

Password check using regex in Java

My application has a feature to check password. I need to get this scenario:
password length 10 ~ 32 .
It has to be a combination of either:
character and numbers
characters and special characters
numbers and special characters
Current code in application:
private boolean isSpecialMixedText(String password)
{
String number = "[0-9]";
String english = "[a-zA-Z]";
String special = "[!##\\$%^&*()~`\\-=_+\\[\\]{}|:\\\";',\\./<>?£¥\\\\]";
Pattern numberPattern = Pattern.compile(number);
Matcher numberMatcher = numberPattern.matcher(password);
Pattern englishPattern = Pattern.compile(english);
Matcher englishMatcher = englishPattern.matcher(password);
Pattern specialPattern = Pattern.compile(special);
Matcher specialMatcher = specialPattern.matcher(password);
return numberMatcher.find() && englishMatcher.find() || specialMatcher.find();
}
Please help me get the combination working
Actually, the regexes look fine. The problem is in this statement:
return numberMatcher.find() && englishMatcher.find() ||
specialMatcher.find();
It actually needs to be something like this:
boolean n = numberMatcher.find();
boolean e = englishMatcher.find();
boolean s = specialMatcher.find();
return (n && e) || (n && s) || (e && s);
And I agree with #adelphus' comment. Your rules for deciding what passwords are acceptable are very English-language-centric.
In my opinion your logic is wrong because you look for combination (so only these characters allowed) of: characters and numbers OR characters and special characters OR numbers and special characters. However with pair of matches like: [0-9] and [a-zA-Z] you are actually looking for a String with some digits and some letter, but it could be also 123ABC#$%#$%$#%#$ (because it has letters and digits).
What you need is something to check, if given string is composed ONLY of of allowed combination of characters. I think you can use one regex here (not too elegant, but effective) like:
^(?:((?=.*[A-Za-z].*)(?=.*[0-9].*)[A-Za-z0-9]{10,32})|((?=.*[-!##\\$%^&*()~`\=_+\[\]{}|:\";',.\/<>?£¥\\].*)(?=.*[0-9].*)[0-9-!##\\$%^&*()~`\=_+\[\]{}|:\";',.\/<>?£¥\\]{10,32})|((?=.*[-!##\\$%^&*()~`\=_+\[\]{}|:\";',.\/<>?£¥\\].*)(?=.*[A-Za-z].*)[-A-Za-z!##\\$%^&*()~`\=_+\[\]{}|:\";',.\/<>?£¥\\]{10,32}))$
DEMO - it show valid and invalid matches.
This is quite long regex, but mainly because of you special character class. This regular expression is composed of three parts with similar structure:
positive lookagead for required characters + character class of
allowed characters
On an example:
(?=.*[A-Za-z].*)(?=.*[0-9].*)[A-Za-z0-9]{10,32}
means that string need to have:
(?=.*[A-Za-z].*) - at least one letter (positive lookahead for letter which could be surrounded by other characters),
(?=.*[0-9].*) - at least one number (positive lookahead for digit which could be surrounded by other characters)
[A-Za-z0-9]{10,32} - from 10 to 32 letters or digits,
in effect, the given password need to have 10 to 32 characters, but both letters and digits, proportion is not important.
Whats more, the ^ at beginning and $ in the end ensure that the whole examined string has such composition.
Also I would agree with others, it is not best idea to restrict allowed character in password like that, but it is your decision.

Regular expression not working as intended

I am using the following regular expression.
(?=.+[a-z])(?=.+[A-Z])(?=.+[^a-zA-Z]).{8,}
my goal is to have a password that has 3 of the 4 properties below
upper case character, lower case character, number, special character
I am using http://rubular.com/r/i26nwsTcaU and http://regexlib.com/RETester.aspx to test the expression with the following inputs
P#55w0rd
password1P
Password1
paSSw0rd
all of these should pass but only the second and fourth are passing at http://rubular.com/r/i26nwsTcaU and all of them pass at http://regexlib.com/RETester.aspx.
I also have the following code that I am using to validate
private void doValidate(String inputStr,String regex) {
Pattern pattern = Pattern.compile(regex);
if(!pattern.matcher(inputStr).matches()){
String errMessage = "";
throw new UniquenessConstraintViolatedException(errMessage);
}
}
this code fails to validate "Password1" which should pass.
as far as the expression goes I understand it like this
must have lower (?=.+[a-z])
must have upper (?=.+[A-Z])
must have non alpha (?=.+[^a-zA-Z])
must be eight characters long .{8,}
can anyone tell me what it is I'm doing wrong.
Thanks in advance.
Essentially, the .+ subexpressions are to blame, they should be .*. Otherwise, the lookahead part looks for lower case, upper case or non-alpha but a character of each corresponding type does not count if it is the first one in string. So, you are validating not the password, but the password with first char truncated. While #Cfreak is not right, he is close - what you are doing would not be possible with normal regex and you would have to use what he suggests. With the lookahead groups - (?=) - it is possible to do what you need. Still, personally I would rather code it like #Cfreak suggests - it is more readable and your intentions are clearer from the code. Complex regular expressions tend to be hard to write but close to impossible to read, debug, or improve after some time.
Your regex right now says you must have 1 or more lowercase characters, followed by 1 or more upper case characters followed by 1 or more upper or lowercase characters, followed by 8 characters or more.
Regex can't do AND unless you specify where a particular character appears. You basically need to split each part of your regex into it's own regex and check each one. You can check the length with whatever string length method Java has (sorry i'm not a java dev so I don't know what it is off my head).
Pseudo code:
if( regexForLower && regexForUpper && regexForSpecial && string.length == 8 ) {
// pass
}
As I said in a comment, position-0 capital letters are being ignored.
Here's a regex to which all four passwords match.
(?=.+\\d)(?=.+[a-z])(?=\\w*[A-Z]).{8,}
I wouldn't use such a regex.
it is hard to understand
hard to debug
hard to extend
you can't do much with its result
If you like to tell the client what is wrong with his password, you have investigate the password again. In real world environments you might want to support characters from foreign locales.
import java.util.*;
/**
Pwtest
#author Stefan Wagner
#date Fr 11. Mai 20:55:38 CEST 2012
*/
public class Pwtest
{
public int boolcount (boolean [] arr) {
int sum = 0;
for (boolean b : arr)
if (b)
++sum;
return sum;
}
public boolean [] rulesMatch (String [] groups, String password) {
int idx = 0;
boolean [] matches = new boolean [groups.length];
for (String g: groups) {
matches[idx] = (password.matches (".*" + g + ".*"));
++idx;
}
return matches;
}
public Pwtest ()
{
String [] groups = new String [] {"[a-z]", "[A-Z]", "[0-9]", "[^a-zA-Z0-9]"};
String [] pwl = new String [] {"P#55w0rd", "password1P", "Password1", "paSSw0rd", "onlylower", "ONLYUPPER", "1234", ",:?!"};
List <boolean[]> lii = new ArrayList <boolean[]> ();
for (String password: pwl) {
lii.add (rulesMatch (groups, password));
}
for (int i = 0 ; i < lii.size (); ++i) {
boolean [] matches = lii.get (i);
String pw = pwl[i];
if (boolcount (matches) < 3) {
System.out.print ("Password:\t" + pw + " violates rule (s): ");
int idx = 0;
for (boolean b: matches) {
if (! b)
System.out.print (groups[idx] + " ");
++idx;
}
System.out.println ();
}
else System.out.println ("Password:\t" + pw + " fine ");
}
}
public static void main (String args[])
{
new Pwtest ();
}
}
Output:
Password: P#55w0rd fine
Password: password1P fine
Password: Password1 fine
Password: paSSw0rd fine
Password: onlylower violates rule (s): [A-Z] [0-9] [^a-zA-Z0-9]
Password: ONLYUPPER violates rule (s): [a-z] [0-9] [^a-zA-Z0-9]
Password: 1234 violates rule (s): [a-z] [A-Z] [^a-zA-Z0-9]
Password: ,:?! violates rule (s): [a-z] [A-Z] [0-9]
Password: Übergrößen345 fine
Password: 345ÄÜö violates rule (s): [a-z] [A-Z]

Categories

Resources