Заменить нечисловую строку пустой

125

Быстрое добавление требований в наш проект. Поле в нашей базе данных для хранения телефонного номера настроено только на 10 символов. Итак, если я получу «(913) -444-5555» или что-то еще, есть ли быстрый способ запустить строку с помощью какой-то специальной функции замены, которую я могу передать ей набор символов, чтобы разрешить?

Regex?

Мэтт Доуди
источник

Ответы:

251

Определенно регулярное выражение:

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

или внутри класса, чтобы избежать постоянного повторного создания регулярного выражения:

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

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

В зависимости от ваших реальных входных данных вам может потребоваться дополнительная логика для выполнения таких действий, как удаление ведущих единиц (для больших расстояний) или чего-либо, что следует за x или X (для расширений).

Джоэл Кохорн
источник
Отлично. Это используется только пару раз, поэтому нам не нужно создавать класс, и что касается ведущего 1, неплохая идея. Но я думаю, что лучше разбираться с этим в каждом конкретном случае, по крайней мере, в этом проекте. Еще раз спасибо - если бы я мог проголосовать еще раз, я бы сделал это.
Мэтт Доуди,
1
Я жду, когда кто-нибудь
опубликует
@Joel Я добавил версию метода расширения ниже. Думаю, комментарии не поддерживают уценку.
Аарон
13
Примечание [^\d]можно упростить до\D
pswg
Объединил этот ответ (кэширование регулярного выражения в классе) с методом расширения, приведенным ниже :)
Винсент Ванкалберг,
73

Вы можете легко сделать это с помощью регулярного выражения:

string subject = "(913)-444-5555";
string result = Regex.Replace(subject, "[^0-9]", ""); // result = "9134445555"
CMS
источник
2
Проголосовали за отличный ответ, но Джоэл победил вас. Тем не менее, спасибо за ответ - мне очень нравится видеть подтверждения из нескольких источников.
Мэтт Доуди,
@JoSmo Честно говоря, Joel's можно довольно просто преобразовать в однострочник. (Но я также проголосовал за: D)
Mage Xy
40

Вам не нужно использовать Regex.

phone = new String(phone.Where(c => char.IsDigit(c)).ToArray())
Усман Зафар
источник
3
Хороший ответ, зачем добавлять больше ссылок на пространство имен RegularExpressions
BTE
1
@BTE, потому что это короткая рука, которую просто используютsystem.linq;
Эрик Миллиот-Мартинес
1
Насколько хорошо это работает по сравнению с решением Regex?
Шавай
2
Добавление теста в тестовый код @ Max-PC для решения LINQ приводит к: StringBuilder: 273 мс, Regex: 2096 мс, LINQ: 658 мс. Медленнее, чем StringBuilder, но все же значительно быстрее, чем Regex. Учитывая, что это эталонный тест 1 000 000 замен, эффективная разница между решениями StringBuilder и LINQ для большинства сценариев, вероятно, незначительна.
Крис Пратт
@ChrisPratt для регулярного выражения, вы каждый раз создавали новое регулярное выражение или повторно использовали существующее? Это может сильно повлиять на производительность.
carlin.scott
23

Вот способ сделать это методом расширения.

public static class Extensions
{
    public static string ToDigitsOnly(this string input)
    {
        Regex digitsOnly = new Regex(@"[^\d]");
        return digitsOnly.Replace(input, "");
    }
}
Аарон
источник
8

Используя методы Regex в .NET, вы должны иметь возможность сопоставить любую нечисловую цифру с помощью \ D, например:

phoneNumber  = Regex.Replace(phoneNumber, "\\D", String.Empty);
Уэс Мейсон
источник
5
Это не совсем так. Вам нужен @ или "\\ D", чтобы избежать \ в регулярном выражении. Кроме того, вы должны использовать String.Empty вместо ""
Брайан
5

Как насчет метода расширения, который не использует регулярное выражение.

Если вы все же придерживаетесь одного из параметров Regex, по крайней мере, используйте его RegexOptions.Compiledв статической переменной.

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

Это основано на ответе Усмана Зафара, преобразованном в группу методов.

Майкл Лэнг
источник
4

для лучшей производительности и меньшего потребления памяти попробуйте следующее:

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

Результат на моем компьютере:
Init ...
Время: 307
Время: 2178

Max-PC
источник
+1 для показа тестов. Интересно, что цикл с StringBuilder превосходит RegEx, хотя я думаю, это имеет смысл, когда RegEx, вероятно, должен пройти через множество правил, чтобы решить, что делать.
Steve In CO
3

Я уверен, что есть более эффективный способ сделать это, но, вероятно, я бы сделал это:

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();
}
Джон Нортон
источник
Это было моим первым инстинктом, и именно поэтому я спросил здесь. RegEx кажется мне гораздо лучшим решением. Но спасибо за ответ!
Мэтт Доуди,
-1

попробуй это

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();
        }
Чарльз Бретана
источник
return newPhone.ToString();вернет "System.Char []". Я думаю, вы имели в виду return new string(newPhone);, но это также отфильтровывает числа 0 и 9 из-за >и <вместо >=и <=. Но даже тогда в строке будут конечные пробелы, потому что newPhonмассив длиннее, чем должен быть.
juharr 02