Eliminar caracteres de la cadena C #

150

¿Cómo puedo eliminar caracteres de una cadena? Por ejemplo: "My name @is ,Wan.;'; Wan".

Me gustaría eliminar los caracteres '@', ',', '.', ';', '\''de esa cadena para que se convierta"My name is Wan Wan"

Wanwan
fuente

Respuestas:

177
var str = "My name @is ,Wan.;'; Wan";
var charsToRemove = new string[] { "@", ",", ".", ";", "'" };
foreach (var c in charsToRemove)
{
    str = str.Replace(c, string.Empty);
}

Pero puedo sugerir otro enfoque si desea eliminar todos los caracteres que no sean letras

var str = "My name @is ,Wan.;'; Wan";
str = new string((from c in str
                  where char.IsWhiteSpace(c) || char.IsLetterOrDigit(c)
                  select c
       ).ToArray());
Albin Sunnanbo
fuente
12
También se puede hacer así, str = cadena nueva (str.Where (x => char.IsWhiteSpace (x) || char.IsLetterOrDigit (x)). ToArray ());
Adnan Bhatti
1
Tuve que buscar esto, string.Empty no crea una cadena para la comparación, por lo que es más eficiente que "". ( stackoverflow.com/questions/151472/… )
Tom Cerul
66
¿Soy el único que obtiene el "Argumento 2: no se puede convertir de 'string' a 'char'" om string. ¿Vacío?
OddDev
2
@OddDev solo debería recibir este error si su matriz que recorre es una lista de caracteres. Si son cadenas, esto debería funcionar
Newteq Developer
3
Además, tenga en cuenta que para que la función "str.Replace" funcione correctamente, el primer parámetro debe ser una "cadena" si desea usar string.Empty como segundo parámetro. Si usa un char (es decir, 'a') como primer parámetro, también necesitará un char como el segundo. De lo contrario, obtendrá el error "Argumento 2: no se puede convertir de 'cadena' a 'char'" mencionado por @OddDev arriba
Leo
68

Sencillo:

String.Join("", "My name @is ,Wan.;'; Wan".Split('@', ',' ,'.' ,';', '\''));
Enigmatividad
fuente
64

Suena como una aplicación ideal para RegEx, un motor diseñado para la manipulación rápida de texto. En este caso:

Regex.Replace("He\"ll,o Wo'r.ld", "[@,\\.\";'\\\\]", string.Empty)
John Melville
fuente
3
Parece que esto sería mucho más eficiente que un enfoque basado en iteradores, especialmente si puede usar un Regex compilado;
Ade Miller
Esta debería ser la respuesta aceptada, especialmente porque, como dijo @AdeMiller, será mucho más eficiente.
Obsidiana
14
Esto no es más rápido que el bucle, es un error común pensar que las expresiones regulares siempre son más rápidas que los bucles. Las expresiones regulares no son mágicas, en su núcleo deben en algún momento iterar a través de la cadena para realizar sus operaciones, y pueden ser mucho más lentas con los gastos generales de la propia expresión regular. Realmente se destacan en lo que respecta a la manipulación extremadamente compleja, donde se necesitarían docenas de líneas de código y múltiples bucles. Al probar la versión compilada de esta expresión regular contra un bucle simple no optimizado 50000 veces, la expresión regular es 6 veces más lenta.
Tony Cheetham
¿Qué pasa con la eficiencia de la memoria? ¿Las expresiones regulares no serán más eficientes en el sentido de nuevas asignaciones de cadenas?
Marek
2
Quizás hablé mal cuando afirmé que RegEx es rápido. A menos que esto esté en el centro de un ciclo muy cerrado, entonces otras consideraciones, como la legibilidad y la capacidad de mantenimiento, probablemente dominarán el rendimiento para una operación pequeña como esta.
John Melville
21

Menos específico a su pregunta, es posible eliminar TODOS los signos de puntuación de una cadena (excepto el espacio) haciendo una lista blanca de los caracteres aceptables en una expresión regular:

string dirty = "My name @is ,Wan.;'; Wan";

// only space, capital A-Z, lowercase a-z, and digits 0-9 are allowed in the string
string clean = Regex.Replace(dirty, "[^A-Za-z0-9 ]", "");

Tenga en cuenta que hay un espacio después de ese 9 para no eliminar espacios de su oración. El tercer argumento es una cadena vacía que sirve para reemplazar cualquier subcadena que no pertenece a la expresión regular.

ThisClark
fuente
19

Comparar varias sugerencias (así como comparar en el contexto de reemplazos de un solo carácter con varios tamaños y posiciones del objetivo).

En este caso particular, dividir en los objetivos y unir en los reemplazos (en este caso, cadena vacía) es el más rápido por al menos un factor de 3. Finalmente, el rendimiento es diferente dependiendo del número de reemplazos, donde están los reemplazos la fuente y el tamaño de la fuente. #ymmv

Resultados

(resultados completos aquí )

| Test                      | Compare | Elapsed                                                            |
|---------------------------|---------|--------------------------------------------------------------------|
| SplitJoin                 | 1.00x   | 29023 ticks elapsed (2.9023 ms) [in 10K reps, 0.00029023 ms per]   |
| Replace                   | 2.77x   | 80295 ticks elapsed (8.0295 ms) [in 10K reps, 0.00080295 ms per]   |
| RegexCompiled             | 5.27x   | 152869 ticks elapsed (15.2869 ms) [in 10K reps, 0.00152869 ms per] |
| LinqSplit                 | 5.43x   | 157580 ticks elapsed (15.758 ms) [in 10K reps, 0.0015758 ms per]   |
| Regex, Uncompiled         | 5.85x   | 169667 ticks elapsed (16.9667 ms) [in 10K reps, 0.00169667 ms per] |
| Regex                     | 6.81x   | 197551 ticks elapsed (19.7551 ms) [in 10K reps, 0.00197551 ms per] |
| RegexCompiled Insensitive | 7.33x   | 212789 ticks elapsed (21.2789 ms) [in 10K reps, 0.00212789 ms per] |
| Regex Insentive           | 7.52x   | 218164 ticks elapsed (21.8164 ms) [in 10K reps, 0.00218164 ms per] |

Arnés de prueba (LinqPad)

(nota: la Perfy Vsson extensiones de tiempo escribí )

void test(string title, string sample, string target, string replacement) {
    var targets = target.ToCharArray();

    var tox = "[" + target + "]";
    var x = new Regex(tox);
    var xc = new Regex(tox, RegexOptions.Compiled);
    var xci = new Regex(tox, RegexOptions.Compiled | RegexOptions.IgnoreCase);

    // no, don't dump the results
    var p = new Perf/*<string>*/();
        p.Add(string.Join(" ", title, "Replace"), n => targets.Aggregate(sample, (res, curr) => res.Replace(new string(curr, 1), replacement)));
        p.Add(string.Join(" ", title, "SplitJoin"), n => String.Join(replacement, sample.Split(targets)));
        p.Add(string.Join(" ", title, "LinqSplit"), n => String.Concat(sample.Select(c => targets.Contains(c) ? replacement : new string(c, 1))));
        p.Add(string.Join(" ", title, "Regex"), n => Regex.Replace(sample, tox, replacement));
        p.Add(string.Join(" ", title, "Regex Insentive"), n => Regex.Replace(sample, tox, replacement, RegexOptions.IgnoreCase));
        p.Add(string.Join(" ", title, "Regex, Uncompiled"), n => x.Replace(sample, replacement));
        p.Add(string.Join(" ", title, "RegexCompiled"), n => xc.Replace(sample, replacement));
        p.Add(string.Join(" ", title, "RegexCompiled Insensitive"), n => xci.Replace(sample, replacement));

    var trunc = 40;
    var header = sample.Length > trunc ? sample.Substring(0, trunc) + "..." : sample;

    p.Vs(header);
}

void Main()
{
    // also see /programming/7411438/remove-characters-from-c-sharp-string

    "Control".Perf(n => { var s = "*"; });


    var text = "My name @is ,Wan.;'; Wan";
    var clean = new[] { '@', ',', '.', ';', '\'' };

    test("stackoverflow", text, string.Concat(clean), string.Empty);


    var target = "o";
    var f = "x";
    var replacement = "1";

    var fillers = new Dictionary<string, string> {
        { "short", new String(f[0], 10) },
        { "med", new String(f[0], 300) },
        { "long", new String(f[0], 1000) },
        { "huge", new String(f[0], 10000) }
    };

    var formats = new Dictionary<string, string> {
        { "start", "{0}{1}{1}" },
        { "middle", "{1}{0}{1}" },
        { "end", "{1}{1}{0}" }
    };

    foreach(var filler in fillers)
    foreach(var format in formats) {
        var title = string.Join("-", filler.Key, format.Key);
        var sample = string.Format(format.Value, target, filler.Value);

        test(title, sample, target, replacement);
    }
}
drzaus
fuente
1
Finalmente algunos números! Buen trabajo @drzaus!
Marek
17
 string x = "My name @is ,Wan.;'; Wan";
 string modifiedString = x.Replace("@", "").Replace(",", "").Replace(".", "").Replace(";", "").Replace("'", "");
mostafa
fuente
Esto no funcionará porque string.Replace devuelve una "cadena modificada". Ver stackoverflow.com/a/13277669/6198927
Esteban Verbel el
8

La forma más sencilla sería usar String.Replace:

String s = string.Replace("StringToReplace", "NewString");
Faizan S.
fuente
6

Otra solución simple:

var forbiddenChars = @"@,.;'".ToCharArray();
var dirty = "My name @is ,Wan.;'; Wan";
var clean = new string(dirty.Where(c => !forbiddenChars.Contains(c)).ToArray());
Paul Van Gundy
fuente
5
new List<string> { "@", ",", ".", ";", "'" }.ForEach(m => str = str.Replace(m, ""));
MirlvsMaximvs
fuente
4

Una cadena es solo una matriz de caracteres, así que use Linq para hacer el reemplazo (similar a Albin anterior, excepto que usa una instrucción linq contiene para hacer el reemplazo):

var resultString = new string(
        (from ch in "My name @is ,Wan.;'; Wan"
         where ! @"@,.;\'".Contains(ch)
         select ch).ToArray());

La primera cadena es la cadena para reemplazar los caracteres y la segunda es una cadena simple que contiene los caracteres

alistair
fuente
La solución Linq de Albin es probablemente mejor, a menos que haya caracteres adicionales que desee filtrar (no cubiertos por espacios en blanco, letras y dígitos).
alistair
3

Bien podría tirar esto aquí.

Haga una extensión para eliminar caracteres de una cadena:

public static string RemoveChars(this string input, params char[] chars)
{
    var sb = new StringBuilder();
    for (int i = 0; i < input.Length; i++)
    {
        if (!chars.Contains(input[i]))
            sb.Append(input[i]);
    }
    return sb.ToString();
}

Y se puede usar así:

string str = "My name @is ,Wan.;'; Wan";
string cleanedUpString = str.RemoveChars('@', ',', '.', ';', '\'');

O simplemente así:

string str = "My name @is ,Wan.;'; Wan".RemoveChars('@', ',', '.', ';', '\'');

fuente
Esta es la mejor solución, ya que realiza la menor cantidad de asignaciones de memoria. También establecería la longitud de la cadena original como la capacidad inicial del generador de cadenas, como: new StringBuilder (input.Length) para este propósito de tener el menor número de asignaciones de memoria.
Treaschf
3

Parece que la forma más corta es combinar LINQ y string.Concat:

var input = @"My name @is ,Wan.;'; Wan";
var chrs = new[] {'@', ',', '.', ';', '\''};
var result = string.Concat(input.Where(c => !chrs.Contains(c)));
// => result = "My name is Wan Wan" 

Vea la demostración de C # . Tenga en cuenta que string.Concates un acceso directo a string.Join("", ...).

Tenga en cuenta que aún es posible construir dinámicamente el uso de una expresión regular para eliminar caracteres conocidos individuales, aunque se cree que la expresión regular es más lenta. Sin embargo, aquí hay una manera de construir una expresión regular tan dinámica (donde todo lo que necesita es una clase de caracteres):

var pattern = $"[{Regex.Escape(new string(chrs))}]+";
var result = Regex.Replace(input, pattern, string.Empty);

Ver otra demostración de C # . La expresión regular se verá así [@,\.;']+(una coincidencia o más ( +) apariciones consecutivas de @, ,, ., ;o 'caracteres) donde el punto no tiene que ser escapado, pero Regex.Escapeserá necesario para escapar de otros caracteres que deben ser escaparon, como \, ^, ]o -cuya posición dentro de la clase de personaje que no puedes predecir.

Wiktor Stribiżew
fuente
la forma linq es horriblemente lenta en algunos casos .
drzaus
3

Aquí hay un método que escribí que tiene un enfoque ligeramente diferente. En lugar de especificar los caracteres a eliminar, le digo a mi método qué caracteres quiero conservar: eliminará todos los demás caracteres.

En el ejemplo del OP, solo quiere mantener caracteres alfabéticos y espacios. Así es como se vería una llamada a mi método ( demo de C # ):

var str = "My name @is ,Wan.;'; Wan";

// "My name is Wan Wan"
var result = RemoveExcept(str, alphas: true, spaces: true);

Aquí está mi método:

/// <summary>
/// Returns a copy of the original string containing only the set of whitelisted characters.
/// </summary>
/// <param name="value">The string that will be copied and scrubbed.</param>
/// <param name="alphas">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="numerics">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="dashes">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="underlines">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="spaces">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="periods">If true, all decimal characters (".") will be preserved; otherwise, they will be removed.</param>
public static string RemoveExcept(string value, bool alphas = false, bool numerics = false, bool dashes = false, bool underlines = false, bool spaces = false, bool periods = false) {
    if (string.IsNullOrWhiteSpace(value)) return value;
    if (new[] { alphas, numerics, dashes, underlines, spaces, periods }.All(x => x == false)) return value;

    var whitelistChars = new HashSet<char>(string.Concat(
        alphas ? "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" : "",
        numerics ? "0123456789" : "",
        dashes ? "-" : "",
        underlines ? "_" : "",
        periods ? "." : "",
        spaces ? " " : ""
    ).ToCharArray());

    var scrubbedValue = value.Aggregate(new StringBuilder(), (sb, @char) => {
        if (whitelistChars.Contains(@char)) sb.Append(@char);
        return sb;
    }).ToString();

    return scrubbedValue;
}
Mass Dot Net
fuente
Respuesta impresionante!
edtheprogrammerguy
¡Muy agradable! la cadena numérica tiene 0 dos veces.
John Kurtz
@JohnKurtz Buena captura: se ha ido ahora.
Mass Dot Net
2

Muchas buenas respuestas aquí, aquí está mi adición junto con varias pruebas unitarias que pueden usarse para ayudar a probar la corrección, mi solución es similar a la de @ Rianne anterior pero usa un ISet para proporcionar tiempo de búsqueda O (1) en los caracteres de reemplazo (y también similar a la solución Linq de @Albin Sunnanbo).

    using System;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Returns a string with the specified characters removed.
    /// </summary>
    /// <param name="source">The string to filter.</param>
    /// <param name="removeCharacters">The characters to remove.</param>
    /// <returns>A new <see cref="System.String"/> with the specified characters removed.</returns>
    public static string Remove(this string source, IEnumerable<char> removeCharacters)
    {
        if (source == null)
        {
            throw new  ArgumentNullException("source");
        }

        if (removeCharacters == null)
        {
            throw new ArgumentNullException("removeCharacters");
        }

        // First see if we were given a collection that supports ISet
        ISet<char> replaceChars = removeCharacters as ISet<char>;

        if (replaceChars == null)
        {
            replaceChars = new HashSet<char>(removeCharacters);
        }

        IEnumerable<char> filtered = source.Where(currentChar => !replaceChars.Contains(currentChar));

        return new string(filtered.ToArray());
    }

Pruebas de NUnit (2.6+) aquí

using System;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;

[TestFixture]
public class StringExtensionMethodsTests
{
    [TestCaseSource(typeof(StringExtensionMethodsTests_Remove_Tests))]
    public void Remove(string targetString, IEnumerable<char> removeCharacters, string expected)
    {
        string actual = StringExtensionMethods.Remove(targetString, removeCharacters);

        Assert.That(actual, Is.EqualTo(expected));
    }

    [TestCaseSource(typeof(StringExtensionMethodsTests_Remove_ParameterValidation_Tests))]
    public void Remove_ParameterValidation(string targetString, IEnumerable<char> removeCharacters)
    {
        Assert.Throws<ArgumentNullException>(() => StringExtensionMethods.Remove(targetString, removeCharacters));
    }
}

internal class StringExtensionMethodsTests_Remove_Tests : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        yield return new TestCaseData("My name @is ,Wan.;'; Wan", new char[] { '@', ',', '.', ';', '\'' }, "My name is Wan Wan").SetName("StringUsingCharArray");
        yield return new TestCaseData("My name @is ,Wan.;'; Wan", new HashSet<char> { '@', ',', '.', ';', '\'' }, "My name is Wan Wan").SetName("StringUsingISetCollection");
        yield return new TestCaseData(string.Empty, new char[1], string.Empty).SetName("EmptyStringNoReplacementCharactersYieldsEmptyString");
        yield return new TestCaseData(string.Empty, new char[] { 'A', 'B', 'C' }, string.Empty).SetName("EmptyStringReplacementCharsYieldsEmptyString");
        yield return new TestCaseData("No replacement characters", new char[1], "No replacement characters").SetName("StringNoReplacementCharactersYieldsString");
        yield return new TestCaseData("No characters will be replaced", new char[] { 'Z' }, "No characters will be replaced").SetName("StringNonExistantReplacementCharactersYieldsString");
        yield return new TestCaseData("AaBbCc", new char[] { 'a', 'C' }, "ABbc").SetName("CaseSensitivityReplacements");
        yield return new TestCaseData("ABC", new char[] { 'A', 'B', 'C' }, string.Empty).SetName("AllCharactersRemoved");
        yield return new TestCaseData("AABBBBBBCC", new char[] { 'A', 'B', 'C' }, string.Empty).SetName("AllCharactersRemovedMultiple");
        yield return new TestCaseData("Test That They Didn't Attempt To Use .Except() which returns distinct characters", new char[] { '(', ')' }, "Test That They Didn't Attempt To Use .Except which returns distinct characters").SetName("ValidateTheStringIsNotJustDistinctCharacters");
    }
}

internal class StringExtensionMethodsTests_Remove_ParameterValidation_Tests : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        yield return new TestCaseData(null, null);
        yield return new TestCaseData("valid string", null);
        yield return new TestCaseData(null, new char[1]);
    }
}
aolszowka
fuente
2

Es un método poderoso que suelo usar en el mismo caso:

private string Normalize(string text)
{
        return string.Join("",
            from ch in text
            where char.IsLetterOrDigit(ch) || char.IsWhiteSpace(ch)
            select ch);
}

Disfrutar...

Mohammad Fathi MiMFa
fuente
1

Old School en el lugar copiar / pisar:

  private static string RemoveDirtyCharsFromString(string in_string)
     {
        int index = 0;
        int removed = 0;

        byte[] in_array = Encoding.UTF8.GetBytes(in_string);

        foreach (byte element in in_array)
        {
           if ((element == ' ') ||
               (element == '-') ||
               (element == ':'))
           {
              removed++;
           }
           else
           {
              in_array[index] = element;
              index++;
           }
        }

        Array.Resize<byte>(ref in_array, (in_array.Length - removed));
        return(System.Text.Encoding.UTF8.GetString(in_array, 0, in_array.Length));
     }

No estoy seguro acerca de la eficiencia con otros métodos (es decir, la sobrecarga de todas las llamadas a funciones y las instancias que ocurren como un efecto secundario en la ejecución de C #).

usuario6262837
fuente
1

Lo hago método de extensión y con la matriz de cadenas, creo que string[]es más útil que char[]porque char también puede ser una cadena:

public static class Helper
{
    public static string RemoverStrs(this string str, string[] removeStrs)
    {
        foreach (var removeStr in removeStrs)
            str = str.Replace(removeStr, "");
        return str;
    }
}

entonces puedes usarlo en cualquier lugar:

string myname = "My name @is ,Wan.;'; Wan";
string result = myname.RemoveStrs(new[]{ "@", ",", ".", ";", "\\"});
Yu Jang Jian
fuente
1

Necesitaba eliminar caracteres especiales de un archivo XML. Así es como lo hice. char.ToString () es el héroe en este código.

string item = "<item type="line" />"
char DC4 = (char)0x14;
string fixed = item.Replace(DC4.ToString(), string.Empty);
Mate
fuente
1
new[] { ',', '.', ';', '\'', '@' }
.Aggregate("My name @is ,Wan.;'; Wan", (s, c) => s.Replace(c.ToString(), string.Empty)); 
Dalsier
fuente
1

Tomando las cifras de rendimiento de @drzaus, aquí hay un método de extensión que utiliza el algoritmo más rápido.

public static class StringEx
{
    public static string RemoveCharacters(this string s, params char[] unwantedCharacters) 
        => s == null ? null : string.Join(string.Empty, s.Split(unwantedCharacters));
}

Uso

var name = "edward woodward!";
var removeDs = name.RemoveCharacters('d', '!');
Assert.Equal("ewar woowar", removeDs); // old joke
Lee Oades
fuente