Регулярное выражение для любого числа, кратного 60, используя C # .Net? - PullRequest
6 голосов
/ 12 марта 2010

Мне нужно применить проверку к временным интервалам ввода, которые принимаются за секунды. Теперь я не очень хорош в регулярных выражениях. Так может любое тело помочь сделать регулярное выражение, которое может проверить, делится ли число на 60.

Мне было интересно, смогу ли я использовать для проверки один, который проверяет, что число делится на 10, а затем проверить, делится ли то же самое на 6.

Для числа, кратного 10, здесь [\ d * 0] - это выражение, которое я предполагаю. Пожалуйста, поправьте меня, если я ошибаюсь.

Надеюсь, кто-нибудь решит мою проблему.

Спасибо

Ответы [ 6 ]

17 голосов
/ 12 марта 2010

Почему бы не сделать N % 60?

Почему вы хотите использовать регулярные выражения для работы, которую легко выполнить одним оператором?

15 голосов
/ 12 марта 2010

Как уже упоминалось, регулярные выражения совершенно не подходят для использования . Вместо этого используйте TryParse, чтобы преобразовать строку в целое число, а затем проверьте, делится ли целое число на 60.

Тем не менее, поучительно посмотреть, как это может работать с регулярным выражением.

Во-первых, из однозначных чисел только 0 делится на 60 равномерно. Из нецифровых чисел все числа, делимые на 60, также делятся на 20 и поэтому заканчиваются на 00, 20, 40. , 60 или 80. Это легко проверить с помощью регулярного выражения.

Предположим, он прошел первый тест. Из этого теста мы знаем, что число делится на 20. Теперь все, что нам нужно сделать, - это показать, что оно делится на 3. Если это так, то оно делится на 20 и 3 и, следовательно, должно делиться на 60, поскольку 20 и 3 взаимно просты.

Итак, нам нужно регулярное выражение, которое может определить, делится ли число на 3.

Хорошо известно, что числа, кратные трем, таковы, что сумма их цифр делится на 3.

Также хорошо известно, что каждому регулярному выражению соответствует конечный автомат, а каждому конечному автомату соответствует регулярное выражение.

Поэтому, если мы сможем придумать конечный автомат, который определяет делимость на три, мы закончили.

Это легко сделать. Наш конечный автомат имеет три состояния: A, B и C. Начальное состояние - A. Принимающее состояние - A. Переходы:

В состоянии A входы 0, 3, 6 и 9 переходят в состояние A. Входы 1, 4 и 7 переходят в состояние B. Входы 2, 5 и 8 переходят в состояние C.

В состоянии B входы 0, 3, 6 и 9 переходят в состояние B. Входы 1, 4 и 7 переходят в состояние C. Входы 2, 5 и 8 переходят в состояние A.

В состоянии C входы 0, 3, 6 и 9 переходят в состояние C. Входы 1, 4 и 7 переходят в состояние A. Входы 2, 5 и 8 переходят в состояние B.

Все остальные входы переходят в состояние ошибки.

И мы закончили; у нас есть конечный автомат, который проверяет делимость на 3. Поэтому мы можем построить регулярное выражение, которое проверяет делимость на 3; если мы объединим это с регулярным выражением, которое проверяет делимость на 20, то мы получим желаемое регулярное выражение. Язык чисел, написанных в десятичной записи, делимых на 60, является обычным языком.

Фактическое построение такого регулярного выражения оставлено в качестве упражнения. (Похоже, это то, что сделал Tiftik.)

Упражнение: можете ли вы придумать регулярное выражение, которое определяет, содержит ли строка десятичное число, которое делится на 70 равномерно? Если сможешь, посмотрим. Если нет, можете ли вы предоставить доказательство того, что такого регулярного выражения не существует? (То есть язык, который я описываю, не является регулярным.)

8 голосов
/ 12 марта 2010

Я придумал это:

^((?=[^147]*(([147][^147]*){3})*$)(?=[^258]*(([258][^258]*){3})*$)|(?=[^147]*(([147][^147]*){3})*[147][^147]*$)(?=[^258]*(([258][^258]*){3})*[258][^258]*$)|(?=[^147]*(([147][^147]*){3})*([147][^147]*){2}$)(?=[^258]*(([258][^258]*){3})*([258][^258]*){2}$))\d*0(?<=[02468][048]|[13579][26]|^0)$

Примечание. Для простоты я не использовал группы без захвата.

Редактировать: более короткая версия:

^(?=[^147]*(?:(?:[147][^147]*){3})*(?:()|([147][^147]*)|((?:[147][^147]*){2}))$)(?=[^258]*(?:(?:[258][^258]*){3})*(?:()|([258][^258]*)|((?:[258][^258]*){2}))$)((?(1)(?(4)|.$)|.$)|(?(2)(?(5)|.$)|.$)|(?(3)(?(6)|.$)|.$))\w*0(?<=[02468]0|^0)$

6 голосов
/ 12 марта 2010

(+, /, -, *) в Regex не используются в качестве математических операторов. Вместо этого используйте JavaScript.

4 голосов
/ 12 сентября 2010

Примечание. Регулярные выражения не являются подходящим инструментом для этого. Это просто для развлечения и посмотреть, как это можно сделать.

Вот регулярное выражение, которое проверяет, делится ли число на 60:

^0$|^(?!0)(?=.*[02468]0$)(?:[0369]|[147](?:[0369]*[147][0369]*[258])*(?:[0369]*[258]|[0369]*[147][0369]*[147])|[258](?:[0369]*[258][0369]*[147])*(?:[0369]*[147]|[0369]*[258][0369]*[258]))*0$

Он работает, проверяя, делится ли число на 3, и использует предпросмотр, чтобы проверить, делится ли оно на 20. Он отличается от регулярного выражения , отправленного tiftik , следующими способами:

  • Это немного короче.
  • Используются группы без захвата.
  • Многие языки (например, Javascript) не поддерживают утверждения обратной длины переменной длины. Это регулярное выражение не использует lookbehinds, поэтому его также можно использовать, например, для проверки на стороне клиента в веб-приложении.
  • Запрещает числа с начальными нулями (если вы хотите разрешить начальные нули, просто удалите (?!0)).

Это код, который я использовал для генерации и тестирования:

using System;
using System.Text;
using System.Text.RegularExpressions;

class Program
{
    Regex createRegex()
    {
        string a = "[0369]*";
        string b = "a[147]";
        string c = "a[258]";
        string r = "^0$|^(?!0)(?=.*[02468]0$)(?:[0369]|[147](?:bc)*(?:c|bb)|[258](?:cb)*(?:b|cc))*0$";
        r = r.Replace("b", b).Replace("c", c).Replace("a", a);
        return new Regex(r);
    }

    bool isDivisibleBy3(string s)
    {
        int sumOfDigits = 0;
        foreach (char c in s)
        {
            sumOfDigits += c - '0';
        }
        return sumOfDigits % 3 == 0;
    }

    bool isDivisibleBy20(string s)
    {
        return Regex.IsMatch(s, "^0$|[02468]0$");
    }

    bool isDivisibleBy60(string s)
    {
        return isDivisibleBy3(s) && isDivisibleBy20(s);
    }

    bool isValid(string s)
    {
        return Regex.IsMatch(s, "^0$|^[1-9][0-9]*$");
    }

    void Run()
    {
        Regex regex = createRegex();
        Console.WriteLine(regex);

        // Test on some random strings.
        Random random = new Random();
        for (int i = 0; i < 100000; ++i)
        {
            int length = random.Next(50);
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j < length; ++j)
            {
                sb.Append(random.Next(10));
            }
            string s = sb.ToString();
            bool isMatch = regex.IsMatch(s);
            bool expected = isValid(s) && isDivisibleBy60(s);
            if (isMatch != expected)
            {
                Console.WriteLine("Failed for " + s);
            }
        }
    }

    static void Main()
    {
        new Program().Run();
    }
}
2 голосов
/ 12 марта 2010

Регулярные выражения - неподходящий инструмент для этой работы. Вместо этого попробуйте разделить на 60 и посмотреть, нет ли остатка:

if (value % 60 == 0) {
  // is divisible by 60
  // ... do something ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...