Reemplazar no numérico con cadena vacía

125

Agregue rápidamente el requisito en nuestro proyecto. Un campo en nuestro DB para contener un número de teléfono está configurado para permitir solo 10 caracteres. Entonces, si me pasa "(913) -444-5555" o cualquier otra cosa, ¿hay una manera rápida de ejecutar una cadena a través de algún tipo de función de reemplazo especial que pueda pasarle un conjunto de caracteres para permitir?

Regex?

Matt Dawdy
fuente

Respuestas:

251

Definitivamente regex:

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

o dentro de una clase para evitar volver a crear la expresión regular todo el tiempo:

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

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

Dependiendo de sus entradas del mundo real, es posible que desee algo de lógica adicional para hacer cosas como eliminar los primeros 1 (para larga distancia) o cualquier cosa detrás de una x o X (para extensiones).

Joel Coehoorn
fuente
Eso es perfecto. Esto solo se usa un par de veces, por lo que no necesitamos crear una clase, y en cuanto al primer 1, no es una mala idea. Pero creo que prefiero manejar eso caso por caso, al menos en este proyecto. Gracias de nuevo, si pudiera votar de nuevo, lo haría.
Matt Dawdy
1
Estoy esperando que alguien publique una versión del método de extensión de esto para la clase de cadena :)
Joel Coehoorn
@ Joo Agregué la versión del método de extensión a continuación. Supongo que los comentarios no admiten rebajas.
Aaron
13
La nota [^\d]se puede simplificar a\D
pswg
Combinó esta respuesta (almacenamiento en caché de la expresión regular en la clase) con el método de extensión uno a continuación :)
Vincent Vancalbergh
73

Puedes hacerlo fácilmente con regex:

string subject = "(913)-444-5555";
string result = Regex.Replace(subject, "[^0-9]", ""); // result = "9134445555"
CMS
fuente
2
Votaron por ser una gran respuesta, pero Joel te venció. Sin embargo, gracias por la respuesta: realmente me gusta ver la confirmación de múltiples fuentes.
Matt Dawdy el
@JoSmo Para ser justos, Joel's se puede convertir en una línea bastante trivial. (Pero también voté: D)
Mage Xy
40

No necesitas usar Regex.

phone = new String(phone.Where(c => char.IsDigit(c)).ToArray())
Usman Zafar
fuente
3
Buena respuesta, ¿por qué agregar más referencia al espacio de nombres RegularExpressions
BTE
1
@BTE porque es una mano corta que simplemente está utilizandosystem.linq;
Eric Milliot-Martinez
1
¿Qué tan bien funciona esto en comparación con la solución Regex?
Shavais
2
Agregar una prueba al código de referencia de @ Max-PC para los resultados de la solución LINQ en: StringBuilder: 273ms, Regex: 2096ms, LINQ: 658ms. Más lento que StringBuilder pero aún significativamente más rápido que Regex. Dado que se trata de una evaluación comparativa de 1,000,000 de reemplazos, la diferencia efectiva entre las soluciones StringBuilder y LINQ para la mayoría de los escenarios es probablemente insignificante.
Chris Pratt
@ChrisPratt para la expresión regular, ¿creaste una nueva expresión regular cada vez o reutilizaste una existente? Eso podría tener un gran impacto en el rendimiento.
carlin.scott
23

Aquí está la forma del método de extensión de hacerlo.

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

Usando los métodos Regex en .NET, debería poder hacer coincidir cualquier dígito no numérico usando \ D, de esta manera:

phoneNumber  = Regex.Replace(phoneNumber, "\\D", String.Empty);
Wes Mason
fuente
55
Esto no está del todo bien. Necesita una @ o "\\ D" para escapar de la \ en la expresión regular. Además, debe usar String.Empty en lugar de ""
Bryan
5

¿Qué tal un método de extensión que no utiliza expresiones regulares?

Si se apega a una de las opciones de Regex, al menos úsela RegexOptions.Compileden la variable estática.

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

Esto se basa en la respuesta de Usman Zafar convertida en un grupo de métodos.

Michael Lang
fuente
4

para obtener el mejor rendimiento y un menor consumo de memoria, intente esto:

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

El resultado en mi computadora es:
Init ...
Hora: 307
Hora: 2178

Max-PC
fuente
+1 para mostrar puntos de referencia. Es interesante que el ciclo con StringBuilder supere a RegEx, aunque supongo que tiene sentido cuando RegEx probablemente tenga que pasar por muchas reglas para decidir qué hacer.
Steve In CO
3

Estoy seguro de que hay una manera más eficiente de hacerlo, pero probablemente haría esto:

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
fuente
Ese fue mi primer instinto, y también fue la razón por la que pregunté aquí. RegEx me parece una solución mucho mejor. ¡Pero gracias por la respuesta!
Matt Dawdy
-1

prueba esto

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
fuente
return newPhone.ToString();devolverá "System.Char []". Creo que quisiste decir return new string(newPhone);, pero esto también está filtrando los números 0 y 9 por el >y en <lugar de >=y <=. Pero incluso entonces, la cadena tendrá espacios finales porque la newPhonmatriz es más larga de lo necesario.
juharr