Объединение этих двух регулярных выражений в одно - PullRequest
11 голосов
/ 27 января 2010

У меня есть следующее в C #:

public static bool IsAlphaAndNumeric(string s)
{
    return Regex.IsMatch(s, @"[a-zA-Z]+") 
        && Regex.IsMatch(s, @"\d+");
}

Я хочу проверить, содержит ли параметр s хотя бы один алфавитный символ и одну цифру, и я написал вышеописанный метод для этого.

Но есть ли способ объединить два регулярных выражения ("[a-zA-Z]+" и "\d+") в одно?

Ответы [ 6 ]

12 голосов
/ 27 января 2010

Для C # с LINQ:

return s.Any(Char.IsDigit) && s.Any(Char.IsLetter);
11 голосов
/ 27 января 2010
@"^(?=.*[a-zA-Z])(?=.*\d)"

 ^  # From the begining of the string
 (?=.*[a-zA-Z]) # look forward for any number of chars followed by a letter, don't advance pointer
 (?=.*\d) # look forward for any number of chars followed by a digit)

Использует два положительных указателя , чтобы убедиться, что он находит одну букву и одно число перед следующим. Вы добавляете ^, чтобы попытаться смотреть вперед только один раз, с начала строки. В противном случае механизм регулярных выражений будет пытаться найти соответствие в каждой точке строки.

3 голосов
/ 27 января 2010

Это не совсем то, что вы хотите, но, скажем, у меня больше времени. Следующее должно работать быстрее, чем регулярное выражение.

    static bool IsAlphaAndNumeric(string str) {
        bool hasDigits = false;
        bool  hasLetters=false;

        foreach (char c in str) {
            bool isDigit = char.IsDigit(c);
            bool isLetter = char.IsLetter(c);
            if (!(isDigit | isLetter))
                return false;
            hasDigits |= isDigit;
            hasLetters |= isLetter;
        }
        return hasDigits && hasLetters;
    }

Почему так быстро, пусть проверит. Ниже приведен генератор тестовой строки. Он генерирует 1/3 заданной полностью правильной строки и 2/3 неправильной рекламы. В 2/3 1/2 - все буквы, а другая половина - все цифры.

    static IEnumerable<string> GenerateTest(int minChars, int maxChars, int setSize) {
        string letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        string numbers = "0123456789";            
        Random rnd = new Random();
        int maxStrLength = maxChars-minChars;
        float probablityOfLetter = 0.0f;
        float probablityInc = 1.0f / setSize;
        for (int i = 0; i < setSize; i++) {
            probablityOfLetter = probablityOfLetter + probablityInc;
            int length = minChars + rnd.Next() % maxStrLength;
            char[] str = new char[length];
            for (int w = 0; w < length; w++) {
                if (probablityOfLetter < rnd.NextDouble())
                    str[w] = letters[rnd.Next() % letters.Length];
                else 
                    str[w] = numbers[rnd.Next() % numbers.Length];                    
            }
            yield return new string(str);
        }
    }

Ниже приводится решение Дарина два. Один скомпилирован, а другой не скомпилирован.

class DarinDimitrovSolution
{
    const string regExpression = @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$";
    private static readonly Regex _regex = new Regex(
        regExpression, RegexOptions.Compiled);

    public static bool IsAlphaAndNumeric_1(string s) {
        return _regex.IsMatch(s);
    }
    public static bool IsAlphaAndNumeric_0(string s) {
        return Regex.IsMatch(s, regExpression);
    }

Ниже приводится основной цикл проверки

    static void Main(string[] args) {

        int minChars = 3;
        int maxChars = 13;
        int testSetSize = 5000;
        DateTime start = DateTime.Now;
        foreach (string testStr in
            GenerateTest(minChars, maxChars, testSetSize)) {
            IsAlphaNumeric(testStr);
        }
        Console.WriteLine("My solution : {0}", (DateTime.Now - start).ToString());

        start = DateTime.Now;
        foreach (string testStr in
            GenerateTest(minChars, maxChars, testSetSize)) {
            DarinDimitrovSolution.IsAlphaAndNumeric_0(testStr);
        }
        Console.WriteLine("DarinDimitrov  1 : {0}", (DateTime.Now - start).ToString());

        start = DateTime.Now;
        foreach (string testStr in
            GenerateTest(minChars, maxChars, testSetSize)) {
            DarinDimitrovSolution.IsAlphaAndNumeric_1(testStr);
        }
        Console.WriteLine("DarinDimitrov(compiled) 2 : {0}", (DateTime.Now - start).ToString());

        Console.ReadKey();
    }

Ниже приведены результаты

My solution : 00:00:00.0170017    (Gold)
DarinDimitrov  1 : 00:00:00.0320032  (Silver medal) 
DarinDimitrov(compiled) 2 : 00:00:00.0440044   (Gold)

Итак, первое решение было лучшим. Еще несколько результатов в режиме релиза и следующие спецификации

   int minChars = 20;
   int maxChars = 50;
   int testSetSize = 100000;

My solution : 00:00:00.4060406
DarinDimitrov  1 : 00:00:00.7400740
DarinDimitrov(compiled) 2 : 00:00:00.3410341 (now that very fast)

Я снова проверил с флагом RegexOptions.IgnoreCase. остальные параметры такие же, как указано выше

My solution : 00:00:00.4290429 (almost same as before)
DarinDimitrov  1 : 00:00:00.9700970 (it have slowed down )
DarinDimitrov(compiled) 2 : 00:00:00.8440844 ( this as well still fast but look at .3 in last result)

После того, как gnarf упомянул, что была проблема с моим алгоритмом, он проверял, состоит ли строка только из букв и цифр, поэтому я изменил ее, и теперь он проверяет, что строка show имеет по крайней мере один символ и одну цифру.

    static bool IsAlphaNumeric(string str) {
        bool hasDigits = false;
        bool hasLetters = false;

        foreach (char c in str) {
            hasDigits |= char.IsDigit(c);
            hasLetters |= char.IsLetter(c);
            if (hasDigits && hasLetters)
                return true;
        }
        return false;
    }

Результаты

My solution : 00:00:00.3900390 (Goody Gold Medal)
DarinDimitrov  1 : 00:00:00.9740974 (Bronze Medal)
DarinDimitrov(compiled) 2 : 00:00:00.8230823 (Silver)

Шахта очень важна.

3 голосов
/ 27 января 2010

Вы можете использовать [a-zA-Z].*[0-9]|[0-9].*[a-zA-Z], но я бы порекомендовал его, только если система, которую вы использовали, принимала только одно регулярное выражение. Я не могу себе представить, что это будет более эффективно, чем два простых шаблона без чередования.

2 голосов
/ 27 января 2010
private static readonly Regex _regex = new Regex(
    @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$", RegexOptions.Compiled);

public static bool IsAlphaAndNumeric(string s)
{
    return _regex.IsMatch(s);
}

Если вы хотите игнорировать регистр, вы можете использовать RegexOptions.Compiled | RegexOptions.IgnoreCase.

0 голосов
/ 27 января 2010

Это не только быстрее, чем другие прогнозные конструкции, но и (на мой взгляд) ближе к требованиям:

[a-zA-Z\d]((?<=\d)[^a-zA-Z]*[a-zA-Z]|[^\d]*\d)

В моем (по общему мнению, грубом тесте) он выполняется примерно вдвое меньше времени, необходимого для других решений регулярных выражений, и имеет преимущество в том, что ему не нужны новые строки во входной строке. (И если по какой-то причине так и должно быть, понятно, как это включить).

Вот как (и почему) это работает:

Шаг 1: соответствует одному символу (назовем его c ), который является цифрой или буквой.
Шаг 2: Он проверяет, является ли c числом. Если так:
Шаг 2.1: он допускает неограниченное количество символов, которые не являются буквой, за которой следует одна буква. Если это соответствует, у нас есть число ( c ), за которым следует буква.
Шаг 2.2: Если c не является числом, это должна быть буква (иначе она не была бы найдена). В этом случае мы допускаем неограниченное количество нецифров, за которыми следует одна цифра. Это будет означать, что у нас есть буква ( c ), за которой следует число.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...