tags:

views:

265

answers:

5

I am trying to create a an function that formats US phone numbers -- hopefully without looping through each digit.

When 10 digits are passed in all is fine. How ever when more than 10 digits are passed in I want the String.Format method to append the extension digits on the right. For example:

When 14 digits passed in the result should be:(444)555-2222 x8888 When 12 digits passed in the result should be:(444)555-2222 x88 etc. However what I get with my current attempt is: Passing in 12 digits returns this string '() -949 x555444433'

here is what I have so far.

public static string _FormatPhone(object phonevalue)
{
    Int64 phoneDigits;

    if (Int64.TryParse(phonevalue.ToString(), out phoneDigits))
    {
        string cleanPhoneDigits = phoneDigits.ToString();
        int digitCount = cleanPhoneDigits.Length;

        if (digitCount == 10)
            return String.Format("{0:(###) ###-####}", phoneDigits);
        else if (digitCount > 10)
            return String.Format("{0:(###) ###-#### x#########}", phoneDigits);
        else
            return cleanPhoneDigits;
    }

    return "Format Err#";
}

Thanks in advance.

+1  A: 

I think you'll have to break your phoneDigits string into the first 10 digits and the remainder.

//[snip]
else if (phoneDigits.ToString().Length > 10)
        {
            return String.Format("{0:(###) ###-#### x}{1}", phoneDigits.Substring(0,10), phoneDigits.Substring(10) );
        }
//[snip]
Ax
Ok, but if you're going to go down the Substring path, why stop there?
Steven Sudit
This seems straight forward enough.else if (phoneDigitString.Length > 10) { return String.Format( "({0}) {1}-{2} x{3}", phoneDigitString.Substring(0, 3), phoneDigitString.Substring(3, 3), phoneDigitString.Substring(3, 4), phoneDigitString.Substring(10)); }
ChiliYago
It's a safe bet that the Substring way would be faster.
Steven Sudit
This works Chili, but what if you have a number like 18005551212? Can a valid phone number start with 1 in the US or is it always used like 1 (800) 555-1212 ? or 1 (212) 515-1212 ?
kiev
A: 

Hi there.

Try using regular expressions:

class Program
    {
        static void Main(string[] args)
        {
            var g = FormatUSPhone("444555222234");

        }

        public static string FormatUSPhone(string num)
        {
            string results = string.Empty;

            if(num.Length == 10)
            {
                num = num.Replace("(", "").Replace(")", "").Replace("-", "");
                const string formatPattern = @"(\d{3})(\d{3})(\d{4})";

                results = Regex.Replace(num, formatPattern, "($1) $2-$3");

            }else if (num.Length == 12)
            {
                num = num.Replace("(", "").Replace(")", "").Replace("-", "");
                const string formatPattern = @"(\d{3})(\d{3})(\d{4})(\d{2})";

                results = Regex.Replace(num, formatPattern, "($1) $2-$3 x$4");
            }

            return results;
        }

I edited the above from an example I found here. Play about with the above code, see if it helps you.

Cheers. Jas.

Jason Evans
I would consider regexp to be complete overkill for this example.
Steven Sudit
@Steven Why, a phone number is after all a regular expression?
David
@David: Because we have all the digits just sitting there. Nothing to search, nothing to match. All we need to do is mix them in with some formatting, and that's easy enough with a StringBuffer.
Steven Sudit
+1  A: 

I'd suggest treating it as a string of digits, not a number. You would then use Substring explicitly to break out the parts.

Steven Sudit
A: 

using a regex:

Regex usPhoneRegex = new Regex(@"(\d{3})(\d{3})(\d{4})(.*)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
string USPhoneFormatString = "$1-$2-$3 x$4";
return usPhoneRegex.Replace("312588230012999", USPhoneFormatString));

Anything after the main phone number will be returned as an extension

Since you were using an int64 in your code, my regex assumes there are no spaces or punctuation in the phone number.

-- Edit -- Ahmad pointed out that I was not handling the case of a number without an extension. So here is a revised version that uses a MatchEvaluator to do the job. Is it better than the other answers? I don't know - but it is a different approach so I thought I would toss it out there.

Regex usPhoneRegex = new Regex(@"(\d{3})(\d{3})(\d{4})(.*)", RegexOptions.IgnoreCase | RegexOptions.Compiled);
return usPhoneRegex.Replace("3125882300", new MatchEvaluator(MyClass.formatPhone))

public static string formatPhone(Match m) {
  int groupIndex = 0;
  string results = string.Empty;
  foreach (Group g in m.Groups) {
    groupIndex +=1;
    switch (groupIndex) {
      case 2 : 
        results = g.Value;
        break;
      case 3 : 
      case 4 : 
        results += "-" + g.Value;
        break;
      case 5 :
        if (g.Value.Length != 0) {
          results += " x " + g.Value;
        }
        break;
    }
  }
  return results;
}

This should probably use a StringBuilder.

Ray
This doesn't handle the case where 10 digits are used and `$4` is empty, which returns the result with a trailing "x": `312-588-2300 x`
Ahmad Mageed
@Ahmad - good point - I am editing my response with another regex approach, using a MatchEvaluator just to see how it works.
Ray
A: 

The problem lies in your else if condition where you have a set number of # placeholders to handle the phone number extension. Instead, we can define the format dynamically to account for different lengths.

Why are you passing in an object? You're using ToString() all over the place. Why not pass in a string from the start? If the item you're passing in isn't a string then call ToString before passing it in, or save the ToString() result in a variable in the method as shown below.

Here's an updated version of your method:

public static string _FormatPhone(object phonevalue)
{
    string returnPhone = "Format Err#";

    Int64 phoneDigits;
    string phoneNumber = phonevalue.ToString();

    if (Int64.TryParse(phoneNumber, out phoneDigits))
    {
        if (phoneNumber.Length == 10)
        {
            return phoneDigits.ToString("(###) ###-####");
        }
        else if (phoneNumber.Length > 10)
        {
            // determine the length of placeholders needed for the format
            string format = "(###) ###-#### x"
                                + new string('#', phoneNumber.Length - 10);
            return phoneDigits.ToString(format);
        }
        else
        {
            return phoneNumber;
        }
    }

    return returnPhone;
}

To test it:

string[] inputs = { "456", "4445552222", "444555222288", "44455522226789" };
foreach (string input in inputs)
{
    Console.WriteLine("Format Result: " + _FormatPhone(input));
}

There's no need for a regex in this case. If you really wanted to use one though, your replacement method needs to determine the length in order to append the extension when needed as shown below:

string[] inputs = { "456", "4445552222", "444555222288", "44455522226789" };
string pattern = @"(\d{3})(\d{3})(\d{4})(\d*)";
foreach (string input in inputs)
{
    string result = Regex.Replace(input, pattern, m =>
    {
        if (m.Value.Length >= 10)
        {
            return String.Format("({0}) {1}-{2}",
                m.Groups[1].Value, m.Groups[2].Value, m.Groups[3].Value)
                    + (m.Value.Length > 10 ? " x" + m.Groups[4].Value : "");
        }
        return m.Value;
    });
    Console.WriteLine("Regex result: " + result);
}
Ahmad Mageed