views:

842

answers:

6

In my C# application, I use a regular expression to validate the basic format of a US phone number to make sure that the user isn't just entering bogus data. Then, I strip out everything except numbers, so this:

(123) 456-7890 x1234

becomes

12345678901234

in the database. In various parts of my application, however, I would like to convert this normalized phone number back to

(123) 456-7890 x1234

What's the best way to do such a thing? (Don't worry about accounting for international phone number formats, by the way.)

+4  A: 

Using a regex you can replace:

(\d{3})(\d{3})(\d{4})(\d{4})

with:

(\1) \2-\3 x\4

(Though I'm not familiar with US phone numbers so maybe there's more to it.)

PEZ
A: 

Do you HAVE to break it down for the DB? If not, don't. If you MUST, then you can either store the different parts in different fields, (Areacode, Prefix, SubscriberNum, Extenion).

Or, extract the number, and begin parsing. If it's only 10 digits, then you know there is no extension. All digits past 10, stick them in the string after an 'x' or something.

I did something similar to this in a C++ app I wrote the stored different contact mechanisms as a single string, but instead, I did the reverse of what you are doing. I took the fields off a dialog, and built the formatted number to store as a string.

LarryF
Unfortunately, I do. The application is an interface to an existing database that has rules about phone number formatting. Believe me, if I could avoid all of this nonsense, I would. :P
David Brown
+2  A: 

I would just use a custom format string to transform the number back into the string:

class Program
{
    static void Main(string[] args)
    {
        long phoneNumber = 12345678901234;
        string phoneNumberString = String.Format("{0:(000) 000-0000 x0000}", phoneNumber);
        Console.WriteLine(phoneNumberString);
    }
}

Of course, you would factor it out into a function which would take the phone number as a long and then return the string (with the format loaded or stored as a constant in the method, or something appropriate for your situation).

Oh, and if you have it in a string and not a long, you can easily convert the string to a long, and then pass it to the format function. Of course, there are performance considerations here if you are doing it repeatedly (since you are iterating the string to create the long, and then converting it back to a string, when you could just use substring).

casperOne
I'm liking this method (since it gives my users the ability to change the mask if they need to). However, will this fail if the input string doesn't have an extension?
David Brown
+5  A: 
String.Format("{0:(###) ###-#### x ###}", double.Parse("1234567890123"))

Will result in (123) 456-7890 x 123

BFree
El neato tricko! =)
PEZ
is that safe to use double? I would use decimal in this case.
FryGuy
Double has enough digits for this purpose, and since there is no fractional part that might cause rounding errors, it will probably "work".Nevertheless it doesn't "feel" right, and in general I would avoid such "tricky" code.
oefe
This code won't work with a leading 0 in the string: 03 9999 9999. Thus it is not an international solution as in Australia we have state codes that begin with 0.
Jonathan Parker
+1  A: 

If you only support US numbers, you could simply format the digits to show parenthesis and x wherever you want.

I would prefer to store the whole string, I would parse it using a regex to validate it, then store it in a normalized string.

To make it accept any country, I would do this:

I would add the IDD code to all phone numbers, and then hide it from users from that country.

so: (123) 456-7890 x1234 would be stored as +1 (123) 456-7890 x1234

The (perl-compatible) regex would be something like (completely untested and wouldn't work) :

(+\d+)?\s+(((\d{,3}))(?\s+([-.0-9]{6,})\s+((x|ext\w*)\d{,4})

  • This is an optional number of digits preceded by +
  • Followed by one or more spaces
  • Then an optional group of up to 3 digits between parenthesis
  • Then one or more spaces
  • Then a group of 6 or more digits, dashes or dots
  • Then one or more spaces
  • Then an optional x or a word that begins with ext (ext, extension ...) and a group of up to 4 digits

I would have a database of users including country and area code, then fill those in automatically in case they're missing, the country would have it's default digit grouping convention for phone numbers (3,4 for the us).

  • So if you're in area 123 in the us, and enter 456.7890, it would be parsed as +1 (123) 4567890, and you would only see it as 456-7890
  • if you're in Qatar and enter the number 4444555 extenshn 33, it is stored as +974 4444555 x33, you would see it as 4444555 x33

The international code will not be displayed for users in the same country, and the area code will not be displayed for users in the same country and area code. The full number would be displayed onmouseover (HTML label?)

Osama ALASSIRY
A: 

Here's an extension method that might help:

public static string InsertStringAtPositions(this string str, string insertStr, IEnumerable<int> positions)
{
    if (str != null && insertStr != null && positions != null)
    {
        string newString = string.Empty;
        int previousPos = 0;
        foreach (var pos in positions)
        {
            if (pos < str.Length)
            {
                newString += str.Substring(previousPos, pos - previousPos) + insertStr;
                previousPos = pos;
            }
        }
        if (positions.Last() < str.Length)
        {
            return newString + str.Substring(positions.Last(), str.Length - positions.Last());
        }
        return newString;
    }
    return str;
}

Usage:

// Will convert "0399998888" to "03 9999 8888"
number.InsertStringAtPositions(" ", new[] {2, 6});
Jonathan Parker