This is a simple question, but please hear me out - A part of a homework assignment for Java has a password validator method. The requirements are simple - password must be between 6-10 characters, must be made only of digits or letters and has to have at least 2 digits in it to be valid. I made this with if statements and using regex, for some reason I cannot get the non-word character regex to match despite every online regex checker showing this should work and even the jetbrains plugin for regex check shows this should be valid. (I also understand this could be done with a one-liner and none of the if statements but this way is simpler for me.
Example input - MyPass123 should be valid, MyPass123# should match the non-word character and should return "Password must consist only of letters and digits" instead this never happens. I am a beginner in Java so it is most likely I am not doing something right even though it is such a simple problem. Thank you in advance!
Method code below:
public static void passswordCheck(String password)
{
if(password.length()<6 || password.length()>10)
{
System.out.printf("Password needs to be between 6 and 10 characters");
}
else if(password.matches("\\W")) \\This should match when input is MyPass123#
{
System.out.printf("Password must consist only of letters and digits");
}
else if(!password.matches("/(\\d).*(\\d)/"))
{
System.out.printf("Password needs to have at least 2 digits");
}
else
{
System.out.printf("Password is valid");
}
}
You're only matching if the string consists of a single character which is non alphanumeric (= [^a-zA-Z0-9_]).
If you want any string which contains at least one non alphanumeric character: .*?\W.*
String#matches always performs a whole match, i.e. the match needs to span the whole string from the first to the last character. To search for a match anywhere in the string, you need to use the find method of a Matcher object instead:
final Pattern nonWordChar = Pattern.compile("\\W");
if (nonWordChar.matcher(password).find()) {
System.out.printf("Password must consist only of letters and digits");
}
…
You will need to do the same with your other regular expressions.
I have tested the below code. Two options are possible, try using the find method as mentioned by Konrad in one of the comments above or handle it in the regex to match a character anywhere in the string.
\\w{6,10} - Matches only the valid passwords which contains word character(A-Za-z0-9)
.*?\\d{2,}.*? - Looks for 2 or more consecutive digits
I have changed it to use Pattern.matches.
import java.util.regex.*;
public class test {
public static void passswordCheck(String password)
{
if(password.length()<6 || password.length()>10)
{
System.out.println("Password needs to be between 6 and 10 characters");
}
else if(!Pattern.matches("\\w{6,10}",password)) //This should match when input is MyPass123#
{
System.out.println("Password must consist only of letters and digits");
}
else if(!Pattern.matches(".*?\\d{2,}.*?",password))
{
System.out.println("Password needs to have at least 2 digits");
}
else
{
System.out.println("Password is valid");
}
}
public static void main(String[] args)
{
passswordCheck("Mypass2");
}
}
Problems in your code:
You have used \W (i.e. [^\w]) which matches non-word character but note that \w matches not only digits and alphabets but also _ which you do not need in the valid password. Therefore, you need to use \p{Alnum}. Alternatively, you can use [A-Za-z0-9]. Also, in order to consider the whole string, the quantifier + is required.
The pattern, \d.*\d matches the string bounded by two digits which can be at any place (i.e. not just at the beginning and at the end) in the password and therefore, you need to match any place, not the whole string. You can understand it from this example. Thus, String#match will be able to match the whole string only when the digits are placed in the beginning and at the end. Therefore, you need to use Matcher#find instead of String#match. Note that you do not need parenthesis ( ) around \d in your regex pattern. The parenthesis ( ) is used to specify capturing group which you do not need for your requirement.
Given below is a demo code addressing all these issues:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main {
public static void main(String[] args) {
// Test strings
String[] arr = { "abcdefghjk", "abc1def2gh", "Ab1Cd2EfGh", "Ab1CdEfGhI", "Ab1Cd2E3Gh", "Ab_ed2EFG3" };
for (String s : arr) {
System.out.println("Validating the password, " + s);
passswordCheck(s);
}
}
public static void passswordCheck(String password) {
if (password.length() < 6 || password.length() > 10) {
System.out.println("Password needs to be between 6 and 10 characters.\n");
} else if (!password.matches("\\p{Alnum}+")) {
System.out.println("Password must consist only of letters and digits.\n");
} else {
Pattern pattern = Pattern.compile("\\d.*\\d");
Matcher matcher = pattern.matcher(password);
if (!matcher.find()) {
System.out.println("Password needs to have at least 2 digits.\n");
} else {
System.out.println("Password is valid\n");
}
}
}
}
Output:
Validating the password, abcdefghjk
Password needs to have at least 2 digits.
Validating the password, abc1def2gh
Password is valid
Validating the password, Ab1Cd2EfGh
Password is valid
Validating the password, Ab1CdEfGhI
Password needs to have at least 2 digits.
Validating the password, Ab1Cd2E3Gh
Password is valid
Validating the password, Ab_ed2EFG3
Password must consist only of letters and digits.
Related
I'm trying to check a String for a particular format in Java. The format must be LetterLetterNumberNumber
Example: js34
If this format is not not entered then my program will throw a format exception. For some reason I'm having a hard time wrapping my head around this.
I considered using String.matches() method but I don't think it would work here.
import java.util.Scanner;
class Main {
public static void main(String[] args) {
String user;
System.out.print("Enter your String: ");
user = (new Scanner(System.in)).nextLine();//format must match LetterLetterNumberNumber
//format checker goes here
}
}
class formatException extends Exception{
public formatException(){
System.out.print(toString());
}
public String toString(){
return "Incorrect format!";
}
}
Like the comment suggested, you can use String.matches() with regex. It'll look something like this:
//format checker goes here
if (!user.matches("^[A-Za-z]{2}\\d{2}$")) {
throw new FormatException();
}
I'll explain the regex. The "^" signifies that we want to match the start of the string. Then "[A-Za-z]" matches any upper- or lowercase letter. "{2}" means we want to match that two times. "\d" matches a digit. (The double backslash means we want an actual backslash and not an escape character like "\n" (newline).) Then the "{2}" again, because we want two digits. And finally "$" matches the end of the string.
Note: I made formatException start with uppercase.
Write a procedure loadDocument(String name) which will load and analyze lines after lines searching for link in every line. The link format is as follows: 5 characters link= (it can be mixed capital and small letters) after which there is a correct identifier. The correct identifier starts from letter (small or capital) follows by zero or more occurrences of letters or digits or underline _. The procedure has to print subsequent identifiers, each one in a separated line. Before printing, the identifiers have to be changed to small letters. The document ends with line with the text eod, which means end of document.
My code:
public static void loadDocument(String name, Scanner scan) {
while(scan.hasNext()) {
String line = scan.nextLine();
if(line.equals("eod")) {
return;
}
else if(line.matches("link="+name) && correctLink(name)) {
String identifier = name.toLowerCase();
System.out.println(identifier);
}
else
continue;
}
}
// accepted only small letters, capital letter, digits and '_' (but not on the begin)
public static boolean correctLink(String link) {
if(link.matches("^[a-zA-Z]+[0]+||[0-9]+||_"))
return true;
else
return false;
}
How to write if line equal to link=, return whatever's after link=?
My problem is in this code:
else if(line.matches("link="+name) && correctLink(name)) {
String identifier = name.toLowerCase();
System.out.println(identifier);
}
For example, if the input is link=abc, I want it to print abc.
First I would suggest that you get used to compare to literal strings "the other way round" - this will save you from a lot NullPointerExceptions (but this is just a side comment):
if ("eod".equals(line))
You can use #Ofer s example (it is generated from https://regex101.com, a nice page to play around with regular expressions and get them explained btw.) but you should use a different regex:
final String regex = "link=([a-z][a-z0-9_]*)";
and a different option for the pattern:
final Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
I used the CASE_INSENSITIVE option to make the "link" also trigger for mixed case writings (like "Link", "liNk" and so on). Therefore I also could skip the uppercase letters from the actual regex, as [a-z] will also match uppercase letters with that option, and that's what was requested.
The "magic" here is that you put the expression that you later want to "read" from the pattern matcher into parenthesis "()" - this marks a "group". Group 0 always gives back the full match (including the "link=" here).
You can play around with that expression at https://regex101.com/r/id2CP2/1
Please don't forget to convert the identifiers (you get them from matcher.group(i)) to lowercase before you output them.
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+$)
I have a string with 6 characters in length. The first character must be a
capital letter, and the last 5 characters must be digits.
I need to write
code to return true if the characters that follow after the capital letter
are digits, and false if they are not.
Here is what I have so far, but when
testing the code, I get an error:
public boolean hasValidDigits(String s)
{
if (Character.isDigit(s.charAt(1-5))) {
return true;
} else {
return false;
}
}
Next time please put the error description.
What you need here is Regex which test the string to the pattern.
i.e.:
return s.matches("[A-Z]{1}[0-9]{5}");
[A-Z]{1}[0-9]{5} means: one capital letter, and 5 digits after.
Check str.matches("[A-Z][0-9]{5}");
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]