ChiliYago
ChiliYago

Reputation: 12279

String.Format Phone Numbers with Extension

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.

Upvotes: 6

Views: 11573

Answers (6)

Pete
Pete

Reputation: 267

Trying to squeeze it into 1 line, I came up with this.

var phoneNumber = "(999) 555-4455 ext123";
phoneNumber = Regex.Replace(phoneNumber, "(.*?)([+]\\d{1,3})?(.*?)(\\d{3})(.*?)(\\d{3})(.*?)(\\d{4})([ ]+)?(x|ext)?(.*?)(\\d{2,5})?(.*?)$", "$2 $4 $6 $8 $10$12").Trim().Replace("ext","x");

If it starts with +# it will leave that alone. It will then look for blocks of numbers. 3,3,4 then it looks for ext or x for extension and another 2-5 numbers. At that point you can format it anyway you like, I chose spaces.

1234567890 -> '123 456 7890'

(123)456.7890 -> '123 456 7890'

+1 (999)555-4455 ext123 -> '+1 999 555 4455 x123'

Upvotes: 1

Jason Evans
Jason Evans

Reputation: 29186

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.

Upvotes: 0

Ray
Ray

Reputation: 21905

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.

Upvotes: 0

Ahmad Mageed
Ahmad Mageed

Reputation: 96477

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);
}

Upvotes: 0

Steven Sudit
Steven Sudit

Reputation: 19620

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

Upvotes: 1

Ax.
Ax.

Reputation: 687

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]

Upvotes: 2

Related Questions