Substituir não numérico por sequência vazia

125

Adição rápida de requisitos em nosso projeto. Um campo em nosso banco de dados para armazenar um número de telefone é definido para permitir apenas 10 caracteres. Portanto, se eu passar "(913) -444-5555" ou qualquer outra coisa, existe uma maneira rápida de executar uma string através de algum tipo de função de substituição especial que eu possa transmitir a ela um conjunto de caracteres para permitir?

Regex?

Matt Dawdy
fonte

Respostas:

251

Definitivamente regex:

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

ou dentro de uma classe para evitar recriar a regex o tempo todo:

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

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

Dependendo das entradas do mundo real, você pode querer alguma lógica adicional para fazer coisas como remover 1s principais (para longa distância) ou qualquer coisa que segue um x ou X (para extensões).

Joel Coehoorn
fonte
Perfeito. Isso é usado apenas algumas vezes, portanto, não precisamos criar uma classe e, até o primeiro 1, não é uma má idéia. Mas acho que prefiro lidar com isso caso a caso, pelo menos neste projeto. Mais uma vez obrigado - se eu pudesse votar novamente, eu o faria.
Matt Dawdy #
1
Eu estou esperando por alguém para postar uma versão método de extensão deste para a classe string :)
Joel Coehoorn
@ Joel Adicionei a versão do método de extensão abaixo. Acho que os comentários não suportam remarcações.
Aaron
13
Nota [^\d]pode ser simplificada para\D
pswg
Combinado esta resposta (cache a expressão regular na classe) com o um método de extensão abaixo :)
Vincent Vancalbergh
73

Você pode fazer isso facilmente com o regex:

string subject = "(913)-444-5555";
string result = Regex.Replace(subject, "[^0-9]", ""); // result = "9134445555"
CMS
fonte
2
Votado por ser uma ótima resposta, mas Joel venceu você. Obrigado pela resposta - eu realmente gosto de ver a confirmação de várias fontes.
Matt Dawdy #
@JoSmo Para ser justo, o Joel's pode ser convertido em uma linha de maneira bastante trivial. (Mas eu também upvoted: D)
Mago Xy
40

Você não precisa usar o Regex.

phone = new String(phone.Where(c => char.IsDigit(c)).ToArray())
Usman Zafar
fonte
3
Resposta agradável, por que adicionar mais referência ao espaço para nome RegularExpressions
BTE
1
@BTE porque é uma mão curta que está simplesmente utilizando #system.linq;
Eric Milliot-Martinez
1
Qual é o desempenho da comparação com a solução Regex?
Shavais
2
A adição de um teste ao código de referência do @ Max-PC para a solução LINQ resulta em - StringBuilder: 273ms, Regex: 2096ms, LINQ: 658ms. Mais lento que o StringBuilder, mas ainda significativamente mais rápido que o Regex. Dado que este é um comparativo de 1.000.000 de substituições, a diferença efetiva entre as soluções StringBuilder e LINQ para a maioria dos cenários é provavelmente negligenciável.
Chris Pratt
@ ChrisPratt para a regex, você criou uma nova regex a cada vez ou reutilizou uma existente? Isso pode ter um grande impacto no desempenho.
carlin.scott 27/04
23

Aqui está a maneira do método de extensão de fazer isso.

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

Usando os métodos Regex no .NET, você poderá corresponder a qualquer dígito não numérico usando \ D, assim:

phoneNumber  = Regex.Replace(phoneNumber, "\\D", String.Empty);
Wes Mason
fonte
5
Isso não está certo. Você precisa de um @ ou "\\ D" para escapar do \ no regex. Além disso, você deve usar String.Empty em vez de ""
Bryan
5

Que tal um método de extensão que não use regex.

Se você se ater a uma das opções de Regex, pelo menos use RegexOptions.Compiledna variável estática.

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

Isso se baseia na resposta de Usman Zafar convertida em um grupo de métodos.

Michael Lang
fonte
4

para obter o melhor desempenho e menor consumo de memória, tente o seguinte:

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

O resultado no meu computador é:
Init ...
Time: 307
Time: 2178

Max-PC
fonte
+1 para mostrar referências. Interessante que o loop com o StringBuilder supere o RegEx, embora eu ache que faz sentido quando o RegEx provavelmente precisar percorrer muitas regras para decidir o que fazer.
Steve Em CO
3

Tenho certeza de que existe uma maneira mais eficiente de fazer isso, mas provavelmente faria isso:

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
fonte
Esse foi o meu primeiro instinto e também foi por isso que perguntei aqui. O RegEx parece uma solução muito melhor para mim. Mas obrigado pela resposta!
224308 Matt Dawdy #
-1

tente isso

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
fonte
return newPhone.ToString();retornará "System.Char []". Eu acho que você quis dizer return new string(newPhone);, mas isso também está filtrando os números 0 e 9 por causa do >e em <vez de >=e <=. Mas, mesmo assim, a string terá espaços à direita, porque a newPhonmatriz é mais longa do que precisa.
juharr 02/09