Regex assistance to match two conditions

I’m trying to confirm that a password contains at least one Capital Letter and One Number. The following search pattern works when it finds a number, but isn’t catching the lack of capital letter.

Thanks for your Regex-pertise!

rg.SearchPattern = "^(?=.*[A-Z])(?=.*\d).+$"

Try this:

(?:.*(\d).*([A-Z]).*)|(?:.*([A-Z]).*(\d).*)

Thanks Eric, but this sample (below) fails…

“legal_updated_by1” ← this returns a result, although it has no Capitals. :frowning:

Split the password into letters. Then loop through the letters to find an uppercase letter and a number. There is no need for a complicated regex.

3 Likes

AI is a fast friend for this…
try this one : ^(?=.*[A-Z])(?=.*\d).*$

Valid passwords (contain at least one capital letter AND one number):

  • Password123 - ✓ (has capital ‘P’ and numbers)
  • 1Abcdef - ✓ (has capital ‘A’ and number ‘1’)
  • abcD123 - ✓ (has capital ‘D’ and numbers)
  • 123abcXYZ - ✓ (has capitals and numbers)
  • A1 - ✓ (minimal valid example)

Invalid passwords:

  • password123 - ✗ (missing capital letter)
  • PASSWORD - ✗ (missing number)
  • abc - ✗ (missing both capital letter and number)
  • 123 - ✗ (missing capital letter)

Hi @Jean-Yves_Pochez , the funny thing is that is the exact expression I started the post with that didn’t work. Here is a more detailed look (at the non-working expression):

Var rg As New RegEx
Var myMatch As RegExMatch
rg.SearchPattern = "^(?=.*[A-Z])(?=.*\d).+$"
myMatch = rg.Search(new_pwd)

If myMatch <> Nil Then
  System.DebugLog(CurrentMethodName + " OK")
Else
  System.DebugLog(CurrentMethodName + " NOT GOOD")
End If
1 Like

never trust an AI !
but it’s not the exact same regex: there is a * in the AI one near the end, and you have a +

As a non-Regex alternative (for others reading this post?), I wrote this Method to check not only that the password contains a number and uppercase/lowercase, but contains more than X letters and the confirmation password are identical:

Protected Function getCheckPasswords(password1 As String, password2 As String, minLength As Integer = 8, mustContainUpperLowercase As Boolean = True, mustContainNumber As Boolean = True) As String
  'test main password
  If password1.Length < minLength Then Return "Password must be at least " + minLength.ToString + " characters"
  
  If mustContainUpperLowercase And CommonStrings.isSameCaseWAD(password1, password1.Lowercase) Then
    Return "Password must contain upper and lowercase"
  End If
  
  If mustContainNumber And _
    password1.IndexOf("0") < 0 And _
    password1.IndexOf("1") < 0 And _ 
    password1.IndexOf("2") < 0 And _
    password1.IndexOf("3") < 0 And _
    password1.IndexOf("4") < 0 And _
    password1.IndexOf("5") < 0 And _
    password1.IndexOf("6") < 0 And _
    password1.IndexOf("7") < 0 And _
    password1.IndexOf("8") < 0 And _
    password1.IndexOf("9") < 0 Then
    Return "Password must contain at least one number"
  End If
  
  If password2 = "" Then Return "Please enter Confirm Password"
  
  'test Confirm Password
  If password2.Length < minLength Then Return "Confirm Password must be at least " + minLength.ToString + " characters"
  
  If mustContainUpperLowercase And CommonStrings.isSameCaseWAD(password2, password2.Lowercase) Then
    Return "Confirm Password must contain upper and lowercase"
  End If
  
  If mustContainNumber And _
    password2.IndexOf("0") < 0 And _
    password2.IndexOf("1") < 0 And _
    password2.IndexOf("2") < 0 And _
    password2.IndexOf("3") < 0 And _
    password2.IndexOf("4") < 0 And _
    password2.IndexOf("5") < 0 And _
    password2.IndexOf("6") < 0 And _
    password2.IndexOf("7") < 0 And _
    password2.IndexOf("8") < 0 And _
    password2.IndexOf("9") < 0 Then
    Return "Confirm Password must contain at least one number"
  End If
  
  If Not CommonStrings.isSameCaseWAD(password1, password2) Then Return "Both Passwords must be the same"
  
  Return ""
      
End Function

I suspect you’re not telling it to be case-sensitive. Try

Rg.options.caseSensitive = True

1 Like

As @Greg_O alluded, the default setting for Xojo’s RegEx is case-insensitive. You can use his recommendation or put a switch directly into the pattern.

(?-i)^(?=.*[A-Z]) ...

Two things: your plan doesn’t ensure that there is a lowercase letter present so even when it’s working, “ABC123” would pass. And it doesn’t take into account anything other than English capitals.

Assuming you want to rectify both of these issues, this is how I’d write it:

^(?=.*\p{Lu})(?=.*\p{Ll})(?=.*\d).*

\p{type} is for identifying unicode. Lu means “uppercase” and Ll is lowercase.

And if you want to ensure length, e.g., at least 8 characters:

^(?=.*\p{Lu})(?=.*\p{Ll})(?=.*\d).{8,}$
1 Like
var rx as new RegEx
var tx as string = "Abbc1"
rx.SearchPattern = "(\p{Lu})(?=.*\d)"
if rx.Search( tx ) isa RegExMatch then
  msgbox "has uppercase and 1 number"
else
  msgbox "not"
end if

That wouldn’t match a case where the digit preceded the uppercase letter.

@David_Cox I can appreciate the non-Regex approach, especially since I’m terrible with it! :slight_smile:

1 Like

And this is exactly why I don’t put much stock in the AI approach; at this point it’s best used for helping me correct my terrible grammar.

@Kem_Tekinay I think you nailed it!

This seems to do the trick:
rg.SearchPattern = "^(?=.*\p{Lu})(?=.*\p{Ll})(?=.*\d).*"

1 Like

David, in your non-regex solution you are testing both the initial password and the confirm password for the same criteria. But - the confirm password must just match the initial password. So there is not need to test it other than it matches the initial password that is only valid if it passes the criteria.

You might say, “…but the user can put data in the confirm password field first.” I’d just test for that - disallow the confirm entry until a valid entry is in the initial pw field.

1 Like