Zastąp wartości nienumeryczne pustym ciągiem

125

Szybkie dodanie wymagań w naszym projekcie. Pole w naszej bazie danych do przechowywania numeru telefonu jest ustawione na dopuszczenie tylko 10 znaków. Tak więc, jeśli zostanie mi przekazany „(913) -444-5555” lub cokolwiek innego, czy istnieje szybki sposób na uruchomienie ciągu za pomocą jakiejś specjalnej funkcji zamiany, na którą mogę przekazać zestaw znaków, aby zezwolić?

Regex?

Matt Dawdy
źródło

Odpowiedzi:

251

Zdecydowanie regex:

string CleanPhone(string phone)
{
    Regex digitsOnly = new Regex(@"[^\d]");   
    return digitsOnly.Replace(phone, "");
}

lub w klasie, aby uniknąć ciągłego ponownego tworzenia wyrażenia regularnego:

private static Regex digitsOnly = new Regex(@"[^\d]");   

public static string CleanPhone(string phone)
{
    return digitsOnly.Replace(phone, "");
}

W zależności od twoich rzeczywistych danych wejściowych możesz potrzebować dodatkowej logiki, aby robić takie rzeczy, jak usuwanie wiodących 1 (dla długich odległości) lub cokolwiek kończącego x lub X (dla rozszerzeń).

Joel Coehoorn
źródło
To idealne. Jest to używane tylko kilka razy, więc nie musimy tworzyć klasy, a jeśli chodzi o wiodącą 1, to niezły pomysł. Ale myślę, że wolałbym zająć się tym indywidualnie dla każdego przypadku, przynajmniej w tym projekcie. Jeszcze raz dziękuję - gdybym mógł ponownie zagłosować za, zrobiłbym to.
Matt Dawdy
1
Czekam, aż ktoś opublikuje wersję metody rozszerzenia tego dla klasy string :)
Joel Coehoorn
@Joel Dodałem wersję metody rozszerzenia poniżej. Chyba komentarze nie obsługują przecen.
Aaron
13
Uwaga [^\d]można uprościć do\D
pswg
Połączyłem tę odpowiedź (buforowanie wyrażenia regularnego w klasie) z metodą rozszerzenia nr 1 poniżej :)
Vincent Vancalbergh
73

Możesz to łatwo zrobić za pomocą wyrażenia regularnego:

string subject = "(913)-444-5555";
string result = Regex.Replace(subject, "[^0-9]", ""); // result = "9134445555"
CMS
źródło
2
Głos za byciem świetną odpowiedzią, ale Joel cię pokonał. Dziękuję jednak za odpowiedź - bardzo lubię widzieć potwierdzenie z wielu źródeł.
Matt Dawdy
@JoSmo Aby być uczciwym, Joel's można przekształcić w jednolinijkowy dość trywialnie. (Ale ja też zagłosowałem za: D)
Mage Xy
40

Nie musisz używać Regex.

phone = new String(phone.Where(c => char.IsDigit(c)).ToArray())
Usman Zafar
źródło
3
Dobra odpowiedź, po co dodawać więcej odniesień do przestrzeni nazw RegularExpressions
BTE
1
@BTE, ponieważ jest to krótka ręka, która po prostu wykorzystujesystem.linq;
Eric Milliot-Martinez
1
Jak dobrze to działa w porównaniu z rozwiązaniem Regex?
Shavais
2
Dodanie testu do kodu testu porównawczego @ Max-PC dla rozwiązania LINQ powoduje - StringBuilder: 273 ms, Regex: 2096 ms, LINQ: 658 ms. Wolniejszy niż StringBuilder, ale nadal znacznie szybszy niż Regex. Biorąc pod uwagę, że jest to test porównawczy wymiany 1000000, efektywna różnica między rozwiązaniami StringBuilder i LINQ dla większości scenariuszy jest prawdopodobnie pomijalna.
Chris Pratt
@ChrisPratt dla wyrażenia regularnego, czy za każdym razem utworzyłeś nowe wyrażenie regularne, czy ponownie użyłeś istniejącego? Może to mieć duży wpływ na wydajność.
carlin.scott
23

Oto sposób, w jaki można to zrobić.

public static class Extensions
{
    public static string ToDigitsOnly(this string input)
    {
        Regex digitsOnly = new Regex(@"[^\d]");
        return digitsOnly.Replace(input, "");
    }
}
Aaron
źródło
8

Używając metod Regex w .NET, powinieneś być w stanie dopasować dowolną cyfrę nieliczbową za pomocą \ D, na przykład:

phoneNumber  = Regex.Replace(phoneNumber, "\\D", String.Empty);
Wes Mason
źródło
5
To nie jest do końca w porządku. Potrzebujesz znaku @ lub „\\ D”, aby uniknąć znaku \ w wyrażeniu regularnym. Powinieneś także użyć String.Empty zamiast „”
Bryan,
5

Co powiesz na metodę rozszerzenia, która nie używa wyrażenia regularnego.

Jeśli trzymasz się jednej z opcji Regex, użyj przynajmniej RegexOptions.Compiledw zmiennej statycznej.

public static string ToDigitsOnly(this string input)
{
    return new String(input.Where(char.IsDigit).ToArray());
}

Opiera się to na odpowiedzi Usmana Zafara przekonwertowanej na grupę metod.

Michael Lang
źródło
4

aby uzyskać najlepszą wydajność i mniejsze zużycie pamięci, spróbuj tego:

using System;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;

public class Program
{
    private static Regex digitsOnly = new Regex(@"[^\d]");

    public static void Main()
    {
        Console.WriteLine("Init...");

        string phone = "001-12-34-56-78-90";

        var sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < 1000000; i++)
        {
            DigitsOnly(phone);
        }
        sw.Stop();
        Console.WriteLine("Time: " + sw.ElapsedMilliseconds);

        var sw2 = new Stopwatch();
        sw2.Start();
        for (int i = 0; i < 1000000; i++)
        {
            DigitsOnlyRegex(phone);
        }
        sw2.Stop();
        Console.WriteLine("Time: " + sw2.ElapsedMilliseconds);

        Console.ReadLine();
    }

    public static string DigitsOnly(string phone, string replace = null)
    {
        if (replace == null) replace = "";
        if (phone == null) return null;
        var result = new StringBuilder(phone.Length);
        foreach (char c in phone)
            if (c >= '0' && c <= '9')
                result.Append(c);
            else
            {
                result.Append(replace);
            }
        return result.ToString();
    }

    public static string DigitsOnlyRegex(string phone)
    {
        return digitsOnly.Replace(phone, "");
    }
}

Wynik w moim komputerze to:
Init ...
Time: 307
Time: 2178

Max-PC
źródło
+1 za pokazywanie testów. Ciekawe, że pętla z StringBuilder przewyższa RegEx, chociaż wydaje mi się, że ma to sens, gdy RegEx prawdopodobnie musi przebrnąć przez wiele reguł, aby zdecydować, co robić.
Steve In CO
3

Jestem pewien, że istnieje bardziej skuteczny sposób, aby to zrobić, ale prawdopodobnie zrobiłbym to:

string getTenDigitNumber(string input)
{    
    StringBuilder sb = new StringBuilder();
    for(int i - 0; i < input.Length; i++)
    {
        int junk;
        if(int.TryParse(input[i], ref junk))
            sb.Append(input[i]);
    }
    return sb.ToString();
}
Jon Norton
źródło
To był mój pierwszy instynkt i dlatego tutaj zapytałem. RegEx wydaje mi się znacznie lepszym rozwiązaniem. Ale dzięki za odpowiedź!
Matt Dawdy
-1

Spróbuj tego

public static string cleanPhone(string inVal)
        {
            char[] newPhon = new char[inVal.Length];
            int i = 0;
            foreach (char c in inVal)
                if (c.CompareTo('0') > 0 && c.CompareTo('9') < 0)
                    newPhon[i++] = c;
            return newPhon.ToString();
        }
Charles Bretana
źródło
return newPhone.ToString();zwróci „System.Char []”. Myślę, że miałeś na myśli return new string(newPhone);, ale to również jest odfiltrowywanie liczb 0 i 9 z powodu >i <zamiast >=i <=. Ale nawet wtedy string będzie miał końcowe spacje, ponieważ newPhontablica jest dłuższa niż powinna.
juharr