8

I have the following to check if the phone number is in the following format (XXX) XXX-XXXX. The below code always return true. Not sure why.

   Match match = Regex.Match(input, @"((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}");

    // Below code always return true
    if (match.Success) { ....}
ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
Nate Pet
  • 44,246
  • 124
  • 269
  • 414
  • 2
    Have you tried starting with a simpler regex (e.g. one to match 3 digits), and then building it up? – Oliver Charlesworth Dec 21 '11 at 20:43
  • 2
    possible duplicate of [A comprehensive regex for phone number validation](http://stackoverflow.com/questions/123559/a-comprehensive-regex-for-phone-number-validation) – msarchet Dec 21 '11 at 20:47
  • What phone numbers is it returning True for where you'd expect it to return False? Do you know that `Regex.Match()` does not require the entire string to match? – Tim Pietzcker Dec 21 '11 at 20:57
  • @TimPietzcker - Tim, what should I use in place of match to check the whole string? – Nate Pet Dec 21 '11 at 21:02

5 Answers5

14

The general complaint about regex patterns for phone numbers is that they require one to put in the truly optional characters as dashes and other items.

Why can't they be optional and have the pattern not care if they are there or not?

The below pattern makes dashes, periods and parenthesis optional for the user and focuses on the numbers as a result using named captures.

The pattern is commented (using the # and spans multiple lines) so use the Regex option IgnorePatternWhitespace unless one removes the comments. For that flag doesn't affect regex processing, it only allows for commenting of the pattern via the # character and line break .

string pattern = @"
^                  # From Beginning of line
(?:\(?)            # Match but don't capture optional (
(?<AreaCode>\d{3}) # 3 digit area code
(?:[\).\s]?)       # Optional ) or . or space
(?<Prefix>\d{3})   # Prefix
(?:[-\.\s]?)       # optional - or . or space
(?<Suffix>\d{4})   # Suffix
(?!\d)             # Fail if eleventh number found";

The above pattern just looks for 10 numbers and ignores any filler characters such as a ( or a dash - or a space or a tab or even a .. Examples are

(555)555-5555 (OK)
5555555555 (ok)
555 555 5555(ok)
555.555.5555 (ok)
55555555556 (not ok - match failure - too many digits)
123.456.789 (failure)

Different Variants of same pattern

Pattern without comments no longer need to use IgnorePatternWhiteSpace:

^(?:\(?)(?<AreaCode>\d{3})(?:[\).\s]?)(?<Prefix>\d{3})(?:[-\.\s]?)(?<Suffix>\d{4})(?!\d)

Pattern when not using Named Captures

^(?:\(?)(\d{3})(?:[\).\s]?)(\d{3})(?:[-\.\s]?)(\d{4})(?!\d)

Pattern if ExplicitCapture option is used

^\(?(?<AreaCode>\d{3})[\).\s]?(?<Prefix>\d{3})[-\.\s](?<Suffix>\d{4})(?!\d)
ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
10

It doesn't always match, but it will match any string that contains three digits, followed by a hyphen, followed by four more digits. It will also match if there's something that looks like an area code on the front of that. So this is valid according to your regex:

%%%%%%%%%%%%%%(999)123-4567%%%%%%%%%%%%%%%%%

To validate that the string contains a phone number and nothing else, you need to add anchors at the beginning and end of the regex:

@"^((\(\d{3}\) ?)|(\d{3}-))?\d{3}-\d{4}$"
Alan Moore
  • 73,866
  • 12
  • 100
  • 156
6

Here is the C# code I use. It is designed to get all phone numbers from a page of text. It works for the following patters: 0123456789, 012-345-6789, (012)-345-6789, (012)3456789 012 3456789, 012 345 6789, 012 345-6789, (012) 345-6789, 012.345.6789

List<string> phoneList = new List<string>();
Regex rg = new Regex(@"\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})");
MatchCollection m = rg.Matches(html);
foreach (Match g in m)
{
    if (g.Groups[0].Value.Length > 0)
        phoneList.Add(g.Groups[0].Value);
}
gadildafissh
  • 2,243
  • 1
  • 21
  • 26
6

Alan Moore did a good explaining what your exp is actually doing. +1

If you want to match exactly "(XXX) XXX-XXXX" and absolutely nothing else, then what you want is

@"^\(\d{3}\) \d{3}-\d{4}$"
minnow
  • 1,091
  • 2
  • 10
  • 19
2

none of the comments above takes care of international numbers like +33 6 87 17 00 11 (which is a valid phone number for France for example). I would do it in a two-step approach: 1. Remove all characters that are not numbers or '+' character 2. Check the + sign is at the beginning or not there. Check length (this can be very hard as it depends on local country number schemes). Now if your number starts with +1 or you are sure the user is in USA, then you can apply the comments above.

Christophe
  • 21
  • 1