views:

649

answers:

6

Hi there,

I need to apply validation on input time intervals that are taken in as seconds. Now i am not really good at Regular expressions. So can any body help making a regular expression that can test whether a number is divisible by 60.

I was wondering if i could use to test one that check that the number is divisible by 10 and then check whether the same is divisible by 6.

For number divisible by 10 here [\d*0] is the expression i guess. Please correct me if i am wrong.

Hope somebody solves my problem.

Thanks

+16  A: 

Why not do N % 60 ?

Why you want to use regex for a job which can easily be done using one operator ?

codaddict
+2  A: 

Regular expressions are the wrong tool for this job. Instead, try dividing by 60 and see if there is no remainder:

if (value % 60 == 0) {
  // is divisible by 60
  // ... do something ...
}
Will
+5  A: 

(+,/,-,*) in Regex are not used as mathematical operators. Use JavaScript instead.

hallie
+6  A: 

I've come up with this:

^((?=[^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)$

Note: I didn't use non-capturing groups for the sake of simplicity.

Edit: Shorter version:

^(?=[^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)$

tiftik
Impressive! Does it work for "long" or just "int"?
dan04
This is a regular expression, they're used on strings.
tiftik
Sounds really that @dan04 doesn't know what he's doing... why a regex...
Dykam
@tiftik Thanks a lot.... I tried it but somehow it didnt work. I guess using javascript is rather a better option or a custom validator may be...Edit: tried the longer one....
Steve Johnson
LMAO ... are you kidding me?
Richard Hein
I understand that JS is by far the most better option to perform a simple mathematical check. i just wondered and wanted to know if regular expressions could be used to perform such checks as well.@tiftik Just tried the shorter one,,,somehow it doesnt work at all . no validation is performed and now the text accepts any value even a string.. probably the expression is not fitting in to my case somehow ..
Steve Johnson
@Steve Johnson: Do you really have to do this by regexes? If it's a string, simply parse it to get an integer. Then simply check if N % 60 is equal to zero, as people said. (and why do i feel like being trolled?)
tiftik
@tiftik Yeah you are right..Thanks.
Steve Johnson
Your second regular expression matches `a20`, but it should fail to match because it is not a valid integer. I think the `\w` should be `\d`.
Mark Byers
insanity! +1 sir
tenfour
+11  A: 

As others have mentioned, regular expressions are completely the wrong tool to use for this. Rather, use TryParse to convert the string to an integer, and then test whether the integer is divisible by 60.

That said, it is instructive to see how this could possibly work with a regexp.

First off, of the one-digit numbers, only 0 is evenly divisible by 60. Of the non-one-digit numbers, all numbers divisible by 60 are also divisible by 20, and therefore end in 00, 20, 40, 60 or 80. That is easily tested with a regexp.

Suppose it passes the first test. We know from this test that the number is divisible by 20. Now all we need to do is show that it is divisible by 3. If it is, then it is divisible by both 20 and 3 and therefore must be divisible by 60, since 20 and 3 are coprime.

So we need a regular expression that can determine if a number is divisible by 3.

It is well known that numbers divisible by three are such that the sum of their digits is divisible by 3.

It is also well known that every regular expression corresponds to a finite state machine, and every finite state machine corresponds to a regular expression.

Therefore if we can come up with a finite state machine that determines divisibility by three, we're done.

This is easily done. Our finite state machine has three states, A, B and C. The start state is A. The accepting state is A. The transitions are:

When in state A, inputs 0, 3, 6 and 9 go to state A. Inputs 1, 4 and 7 go to state B. Inputs 2, 5 and 8 go to state C.

When in state B, inputs 0, 3, 6 and 9 go to state B. Inputs 1, 4 and 7 go to state C. Inputs 2, 5 and 8 go to state A.

When in state C, inputs 0, 3, 6 and 9 go to state C. Inputs 1, 4 and 7 go to state A. Inputs 2, 5 and 8 go to state B.

All other inputs go to an error state.

And we're done; we have a finite state machine that checks for divisibility by 3. Therefore we can build a regexp that checks for divisibility by 3; if we combine that with the regexp that checks for divisibility by 20, then we have the desired regular expression. The language of numbers written in decimal notation divisible by 60 is a regular language.

The actual construction of such a regular expression is left as an exercise. (Looks like that's what tiftik has done.)

Exercise: can you come up with a regular expression that determines if string contains a decimal number that is evenly divisible by 70? If you can, let's see it. If not, can you provide a proof that no such regular expression exists? (That is, that the language I am describing is not regular.)

Eric Lippert
The known rules for testing for divisibility by 7 are amazingly cumbersome however, Gustavo Gerald Toja Frachia of São Paulo University has a method which can probably be adapted into a regular expression: 1) Separate the number into pairs of digits, starting from the right. 2) Calculate the difference between each pair of digits and the nearest upper or lower multiple of 7, beginning with the first pair at the right. 3) Write out the resulting digits in the order in which they were calculated. 4) Repeat the process on the result. If the final pair is divisible by 7 - so is the original value.
LBushkin
Such a regular expression would be exceedingly complex, I imagine, as it would require branch points for each possible combination of pairs of two digit numbers, as well as two digit and one digit combinations. So I will postulate that divisibility by 70 is indeed regular - if I get some time I may try to write a C# program that generates such a regular expression - doing it by hand doesn't sound like fun :)
LBushkin
I'm impressed. I'm also *never* going to use this. It's a canonical anecdote of regexp abuse.
Steven Sudit
+3  A: 

Note: Regular expressions are not the right tool for this. This is just for fun and to see how it could be done.

Here is a regular expression that tests if a number is divisible by 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$

It works by testing if the number is divisible by 3 and using a lookahead to check if it is divisble by 20. It differs from the regular expression posted by tiftik in the following ways:

  • It is slightly shorter.
  • It uses non-capturing groups.
  • Many languages (for example Javascript )don't support variable length lookbehind assertions. This regular expression does not use lookbehinds so it can for example also be used for client-side validation in a web application.
  • It disallows numbers with leading zeros (if you want to allow leading zeros just remove the (?!0)).

This is the code I used to generate and test it:

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();
    }
}
Mark Byers