Home > Software engineering >  Check patterns of 5 in password "12345", "abcde"
Check patterns of 5 in password "12345", "abcde"

Time:12-02

I'm trying to validate a password string in a .NET for sequential patterns (forward or reverse) with numbers or letters of 5 or more.

Examples of patterns that will not be accepted:

"ABCDE",
"12345",
"54321",
"edcba"

I cannot find a decent regex pattern that handles finding the characters in order, currently just returning any sequence of 5 letters or numbers:

    public bool CheckForSequence(string input)
    {
        return Regex.IsMatch(input.ToUpper(), @"([A-Z])(?!\1)([A-Z])(?!\1|\2)([A-Z])(?!\1|\2|\3)([A-Z])(?!\1|\2|\3|\4)([A-Z])") || 
            Regex.IsMatch(input, @"([1-9])(?!\1)([1-9])(?!\1|\2)([1-9])(?!\1|\2|\3)([1-9])(?!\1|\2|\3|\4)([1-9])");
    }

CodePudding user response:

There are probably way better ways to do this, but, just for fun, I've made a simple brute-force algorithm:


bool CheckForSequence(string inp) {
    bool InRange(int c) {
        const int minLower = (int)'a';
        const int maxLower = (int)'z';
        const int minUpper = (int)'A';
        const int maxUpper = (int)'Z';
        const int minNumber = (int)'0';
        const int maxNumber = (int)'9';
        return (c >= minLower && c <= maxLower) || (c >= minUpper && c <= maxUpper) || (c >= minNumber && c <= maxNumber);
    }
    if(inp.Length < 5) return false;        
    
    for(var i = 0; i < inp.Length - 4; i  )
    {
        var c = (int)inp[i];
        if(InRange(c))
        {
            var vM = c;
            int x;
            for(x = i 1; x < i   5; x  )
            {
                if(inp[x] != vM 1 || !InRange(inp[x])) break;
                vM  ;
            }
            if(x == i 5) return true;               
            for(x = i 1; x < i   5; x  )
            {
                if(inp[x] != vM-1 || !InRange(inp[x])) break;
                vM--;
            }
            if(x == i 5) return true;
        }
    }
    return false;
}

You can see it in action in this fiddle

CodePudding user response:

Wiktor is correct - regex is the wrong tool for this.

Here's one possible implementation:

public static class SequenceChecker
{
    private static char MapChar(char c) => c switch
    {
        >= '0' and <= '9' => c,
        >= 'A' and <= 'Z' => c,
        >= 'a' and <= 'z' => (char)(c - 'a'   'A'),
        _ => default,
    };
    
    private static bool IsSequence(ReadOnlySpan<char> input)
    {
        char x = MapChar(input[0]);
        if (x == default) return false;
        
        char y = MapChar(input[1]);
        if (y == default) return false;
        
        int direction = y - x;
        if (Math.Abs(direction) != 1) return false;
        
        for (int index = 2; index < input.Length; index  )
        {
            x = y;
            y = MapChar(input[index]);
            if (y == default) return false;
            
            int nextDirection = y - x;
            if (nextDirection != direction) return false;
        }
        
        return true;
    }
    
    public static bool ContainsSequence(string input, int sequenceLength = 5)
    {
        if (sequenceLength < 2) throw new ArgumentOutOfRangeException(nameof(sequenceLength));
        
        if (input is null) return false;
        if (input.Length < sequenceLength) return false;
        
        for (int startIndex = 0; startIndex < 1   input.Length - sequenceLength; startIndex  )
        {
            if (IsSequence(input.AsSpan(startIndex, sequenceLength)))
            {
                return true;
            }
        }
        
        return false;
    }
}

CodePudding user response:

Just to add to the plethora of solutions posted so far:

public static int LongestAscendingOrDescendingRun(string s)
{
    if (s.Length <= 1)
        return 0;

    int  longest = 0;
    int  current = 0;
    bool ascending = false;

    for (int i = 1; i < s.Length; i  )
    {
        bool isAscending () => s[i]-s[i-1] ==  1;
        bool isDescending() => s[i]-s[i-1] == -1;

        if (current > 0)
        {
            if (ascending)
            {
                if (isAscending())
                {
                    longest = Math.Max(longest,   current);
                }
                else // No longer ascending.
                {
                    current = 0;
                }
            }
            else // Descending.
            {
                if (isDescending())
                {
                    longest = Math.Max(longest,   current);
                }
                else // No longer descending.
                {
                    current = 0;
                }
            }
        }
        else // No current.
        {
            if (isAscending())
            {
                ascending = true;
                current = 2;
                longest = Math.Max(longest, current);
            }
            else if (isDescending())
            {
                ascending = false;
                current = 2;
                longest = Math.Max(longest, current);
            }
        }
    }
        
    return longest;
}

CodePudding user response:

Like Wiktor has already said, regex isn't a good way to do this. You could find the difference between consecutive characters of the string, and complain if you find a sequence of four or more ones (or -1s).

public bool CheckForSequence(string pass)
{
    int curr_diff = 0; // The difference between the i-1th and i-2th character
    int consec_diff = 0; // The number of consecutive pairs having the same difference
    for (int i = 1; i < pass.Length; i  )
    {
        int diff = pass[i] - pass[i - 1]; // The difference between the ith and i-1th character

        if (Math.Abs(diff) == 1 && curr_diff == diff)
        {
            // If the difference is the same, increment consec_diff
            // And check if the password is invalid
            consec_diff  ;
            if (consec_diff >= 4)
                return false;
        }
        else
        {
            // New diff. reset curr_diff and consec_diff
            curr_diff = diff;
            consec_diff = Math.Abs(diff)==1 ? 1 : 0;
            // If the difference is 1, set consec_diff to 1 else 0
        }
    }

    return consec_diff < 4;
}
  • Related