tags:

views:

665

answers:

13

I have a string when a telephone number is inputted - there is a mask so it always looks like "(123) 456-7890" - I'd like to take the formatting out before saving it to the DB.

How can I do that?

+26  A: 

One possibility using linq is:

string justDigits = new string(s.Where(c => char.IsDigit(c)).ToArray());
Matt Greer
+1 nice, you where a little faster on the draw.
Chuck Conway
Except when i actually run it, it's not working. Just a second...
Matt Greer
`SkipWhile` won't work for this.
LukeH
Very bad answer. This will only remove the first `(` in the OP’s example.
Timwi
Yeah `SkipWhile` does not work. But `Where` does.
Matt Greer
I’ll upvote if you change `IsNumber` to `IsDigit`.
Timwi
@Timwi - that's done
Joel Coehoorn
pair programming over stackoverflow :)
Matt Greer
God - i love linq. I think ill keep it as a string - the posts under me have me worried about overflowsyours did not quite work, so I ended up going with - var number = new string(numberString.Where(c => char.IsNumber(c)).ToArray());
Jerrold
I'd be interested to see whether the Linq solution or the Regex solution has better performance characteristics. :-/
Toby
@Toby: The Linq solution seems about four times faster in my benchmark, but even much faster is the for-loop as in the answer of EKS (five times faster than Linq).
0xA3
+1: Clear and elegant. It's even clearer than the RegEx answer (IMO) because it spells out IsDigit rather than depending on the terse \d. (Ah, LINQ. I'm still trudging along with .NET 2.0 as you can guess by the snippet in my answer.)
Paul Sasik
Love the elegance in this solution!
Mark LeMoine
+1  A: 

You could use a regular expression or you could loop over each character and use char.IsNumber function.

Chuck Conway
+4  A: 

You can make it work for that number with the addition of a simple regex replacement, but I'd look out for higher initial digits. For example, (876) 543-2019 will overflow an integer variable.

Joel Coehoorn
+1 Good point. I didn't think of the that.
Chuck Conway
Hmm ... some part of me really wants to dial that number and see if it's real. I just don't know what I'd say if anyone answered.
qstarin
It's even worse with numbers starting with `+` or `00`...
viraptor
@viraptor - somehow I think he'll only see US numbers
Joel Coehoorn
A: 

You would be better off using regular expressions. An int by definition is just a number, but you desire the formatting characters to make it a phone number, which is a string.

There are numerous posts about phone number validation, see http://stackoverflow.com/questions/123559/a-comprehensive-regex-for-phone-number-validation for starters.

JYelton
I misread the question, but the same information applies. If you're *receiving* the extra characters, you can use regular expressions to remove them. I would still store the phone number as a string, since leading zeroes which might be a valid part of a phone number would otherwise be removed as an int.
JYelton
+21  A: 

You can use a regular expression to remove all non-digit characters:

string phoneNumber = "(123) 456-7890";
phoneNumber = Regex.Replace(phoneNumber, @"[^\d]", "");

Then further on - depending on your requirements - you can either store the number as a string or as an integer. To convert the number to an integer type you will have the following options:

// throws if phoneNumber is null or cannot be parsed
long number = Int64.Parse(phoneNumber, NumberStyles.Integer, CultureInfo.InvariantCulture);

// same as Int64.Parse, but returns 0 if phoneNumber is null
number = Convert.ToInt64(phoneNumber);

// does not throw, but returns true on success
if (Int64.TryParse(phoneNumber, NumberStyles.Integer, 
       CultureInfo.InvariantCulture, out number))
{
    // parse was successful
}
0xA3
`[^\d]` should be the same as just `\D`?
lasseespeholt
Yes, it’s the same. It makes no difference, except that `[^\d]` is more explicit and requires less learning.
Timwi
You have to learn about `[^]`. I'd argue `\D` requires less learning.
recursive
+2  A: 

Try this:

string s = "(123) 456-7890";
UInt64 i = UInt64.Parse(
    s.Replace("(","")
     .Replace(")","")
     .Replace(" ","")
     .Replace("-",""));

You should be safe with this since the input is masked.

Paul Sasik
if the number is `"(543) 231-2322"` you would overflow `int32`.
lasseespeholt
Good point. Replaced int with UInt64
Paul Sasik
You do realize that he doesn't actually want a number, right? The `int.TryParse` was just his initial guess -- it's a red herring.
Gabe
@Gabe: I know, it doesn't make sense. Especially since the input is masked and validated in the UI. It's a silly question with mostly silly answers. I think people just had fun with it. For example, the for loop solution garnered six votes... Why? Geek throwback upvoting? Entertainment. It is Friday.
Paul Sasik
It's not a silly question at all. He has an input string with formatting and he wants to know how to remove that formatting. The only silly thing is that everybody thinks he wants an integer because he thought that int.TryParse might somehow solve his problem.
Gabe
+3  A: 
string digits = Regex.Replace(formatted, @"\D", String.Empty, RegexOptions.Compiled);
Toby
A: 

As many answers already mention, you need to strip out the non-digit characters first before trying to parse the number. You can do this using a regular expression.

Regex.Replace("(123) 456-7890", @"\D", String.Empty) // "1234567890"

However, note that the largest positive value int can hold is 2,147,483,647 so any number with an area code greater than 214 would cause an overflow. You're better off using long in this situation.

Leading zeros won't be a problem for North American numbers, as area codes cannot start with a zero or a one.

C P1R8
A: 

Alternative using Linq:

string phoneNumber = "(403) 259-7898";
var phoneStr = new string(phoneNumber.Where(i=> i >= 48 && i <= 57).ToArray());
Holystream
the "magic numbers" part of this solution should scare everyone away.
Neil N
+8  A: 

Since nobody did a for loop.

long GetPhoneNumber(string PhoneNumberText)
{
    // Returns 0 on error

    StringBuilder TempPhoneNumber = new StringBuilder(PhoneNumberText.Length);
    for (int i=0;i<PhoneNumberText.Length;i++)
    {
        if (!char.IsDigit(PhoneNumberText[i]))
            continue;

        TempPhoneNumber.Append(PhoneNumberText[i]);
    }

    PhoneNumberText = TempPhoneNumber.ToString();
    if (PhoneNumberText.Length == 0)
        return 0;// No point trying to parse nothing

    long PhoneNumber = 0;
    if(!long.TryParse(PhoneNumberText,out PhoneNumber))
        return 0; // Failed to parse string

    return PhoneNumber;
}

used like this:

long phoneNumber = GetPhoneNumber("(123) 456-7890"); 


Update
As pr commented many countries do have zero's in the begining of the number, if you need to support that, then you have to return a string not a long. To change my code to do that do the following:

1) Change function return type from long to string.
2) Make the function return null instead of 0 on error
3) On successfull parse make it return PhoneNumberText

EKS
Thanks fixed.And yea this should preform well, and should be robust if your parsing user input.
EKS
He's storing a phone number. Why would you *ever* want to store a phone number as an integer?
Gabe
I kind of think opposite.. Why would he NOT store it as a number
EKS
@EKS: Perhaps because many countries use phone numbers with leading zeros, and those zeros would be dropped if the phone number was parsed/stored as an actual number.
LukeH
Good point, i have updated my reply with information incase he needs to support that.
EKS
+2  A: 

Aside from all of the other correct answers, storing phone numbers as integers or otherwise stripping out formatting might be a bad idea.

Here are a couple considerations:

  • Users may provide international phone numbers that don't fit your expectations. See these examples So the usual groupings for standard US numbers wouldn't fit.
  • Users may NEED to provide an extension, eg (555) 555-5555 ext#343 The # key is actually on the dialer/phone, but can't be encoded in an integer. Users may also need to supply the * key.
  • Some devices allow you to insert pauses (usually with the character P), which may be necessary for extensions or menu systems, or dialing into certain phone systems (eg, overseas). These also can't be encoded as integers.

[EDIT]

It might be a good idea to store both an integer version and a string version in the database. Also, when storing strings, you could reduce all punctuation to whitespace using one of the methods noted above. A regular expression for this might be:

// (222) 222-2222 ext# 333   ->   222 222 2222 # 333
phoneString = Regex.Replace(phoneString, @"[^\d#*P]", " ");

// (222) 222-2222 ext# 333   ->   2222222222333 (information lost)
phoneNumber = Regex.Replace(phoneString, @"[^\d]", "");

// you could try to avoid losing "ext" strings as in (222) 222-2222 ext.333 thus:
phoneString = Regex.Replace(phoneString, @"ex\w+", "#");
phoneString = Regex.Replace(phoneString, @"[^\d#*P]", " ");
Kimball Robinson
A: 

This is basically a special case of http://stackoverflow.com/questions/1329961/c-removing-common-invalid-characters-from-a-string-improve-this-algorithm/. Where your formatng incl. White space are treated as "bad characters"

Rune FS
+3  A: 

An answer from simpler times, by a man with a ton of simple, one might even say simple-ton:

    public static string Strip(this string phoneNumber)
    {
        return new string(
            new[]
                {
                 // phoneNumber[0],     (
                    phoneNumber[1],  // 6
                    phoneNumber[2],  // 1
                    phoneNumber[3],  // 7
                 // phoneNumber[4],     )
                 // phoneNumber[5],   
                    phoneNumber[6],  // 8
                    phoneNumber[7],  // 6
                    phoneNumber[8],  // 7
                 // phoneNumber[9],     -
                    phoneNumber[10], // 5
                    phoneNumber[11], // 3
                    phoneNumber[12], // 0
                    phoneNumber[13]  // 9
                });
    }

Performance should be spicy yet fresh.

Sam Pearson