Rimuovi i caratteri dalla stringa C #


150

Come posso rimuovere i caratteri da una stringa? Ad esempio: "My name @is ,Wan.;'; Wan".

Vorrei rimuovere i caratteri '@', ',', '.', ';', '\''da quella stringa in modo che diventi"My name is Wan Wan"

Risposte:


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

Ma posso suggerire un altro approccio se si desidera rimuovere tutti i caratteri non lettera

var str = "My name @is ,Wan.;'; Wan";
str = new string((from c in str
                  where char.IsWhiteSpace(c) || char.IsLetterOrDigit(c)
                  select c
       ).ToArray());

12
Può anche essere fatto in questo modo, str = new string (str.Where (x => char.IsWhiteSpace (x) || char.IsLetterOrDigit (x)). ToArray ());
Adnan Bhatti,

1
Ho dovuto cercare questo, string.Empty non crea una stringa per il confronto, quindi è più efficiente di "". ( Stackoverflow.com/questions/151472/... )
Tom cerul

6
Sono l'unico che ottiene "Argomento 2: impossibile convertire da 'stringa' a 'char'" om stringa. Vuoto?
OddDev,

2
@OddDev dovresti ottenere questo errore solo se l'array che attraversi è un elenco di caratteri. Se sono stringhe questo dovrebbe funzionare
Newteq Developer

3
Inoltre, si noti che affinché la funzione "str.Replace" funzioni correttamente, il primo parametro deve essere una "stringa" se si desidera utilizzare string.Empty come secondo parametro. Se si utilizza un carattere (ad es. "A") come primo parametro, sarà necessario anche un carattere come secondo. Altrimenti, otterrai l'errore "Argomento 2: impossibile convertire da 'stringa' a 'char'" menzionato da @OddDev sopra
Leo

68

Semplice:

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

64

Sembra un'applicazione ideale per RegEx, un motore progettato per una rapida manipolazione del testo. In questo caso:

Regex.Replace("He\"ll,o Wo'r.ld", "[@,\\.\";'\\\\]", string.Empty)

3
Sembra che questo sarebbe molto più efficiente di un approccio basato su iteratore soprattutto se è possibile utilizzare un Regex compilato;
Ade Miller,

Questa dovrebbe essere la risposta accettata, soprattutto perché, come ha detto @AdeMiller, sarà molto più efficiente.
Obsidian,

14
Questo non è più veloce del loop, è un'idea sbagliata comune che i regex siano sempre più veloci dei loop. I regex non sono magici, in sostanza devono a un certo punto scorrere la stringa per eseguire le loro operazioni, e possono essere molto più lenti con le spese generali della regex stessa. Eccellono davvero quando si tratta di manipolazioni estremamente complesse, in cui sarebbero necessarie dozzine di righe di codice e cicli multipli. Testando la versione compilata di questo regex contro un semplice ciclo non ottimizzato 50000 volte, il regex è 6 volte più lento.
Tony Cheetham,

Che dire dell'efficienza della memoria? Le espressioni regolari non saranno più efficienti nel senso delle allocazioni di nuove stringhe?
Marek,

2
Forse ho sbagliato a dire quando ho affermato che RegEx è veloce. A meno che questo non sia al centro di un circuito molto ristretto, allora altre considerazioni, una tale leggibilità e manutenibilità probabilmente avranno il sopravvento sulle prestazioni per una piccola operazione come questa.
John Melville,

21

Meno specifico della tua domanda, è possibile rimuovere TUTTA la punteggiatura da una stringa (tranne lo spazio) elencando in bianco i caratteri accettabili in un'espressione regolare:

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

Nota che c'è uno spazio dopo quel 9 in modo da non rimuovere gli spazi dalla frase. Il terzo argomento è una stringa vuota che serve a sostituire qualsiasi sottostringa che non appartiene all'espressione regolare.


19

Confronto di vari suggerimenti (nonché confronto nel contesto di sostituzioni di singoli caratteri con varie dimensioni e posizioni del bersaglio).

In questo caso particolare, dividere gli obiettivi e unire le sostituzioni (in questo caso, stringa vuota) è il più veloce di almeno un fattore 3. In definitiva, le prestazioni sono diverse a seconda del numero di sostituzioni, in cui le sostituzioni sono la fonte e la dimensione della fonte. #ymmv

risultati

(risultati completi qui )

| 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] |

Test Harness (LinqPad)

(nota: le Perfe Vssono estensioni temporali che ho scritto )

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

1
Finalmente alcuni numeri! Ottimo lavoro @drzaus!
Marek,



6

Un'altra soluzione semplice:

var forbiddenChars = @"@,.;'".ToCharArray();
var dirty = "My name @is ,Wan.;'; Wan";
var clean = new string(dirty.Where(c => !forbiddenChars.Contains(c)).ToArray());

5
new List<string> { "@", ",", ".", ";", "'" }.ForEach(m => str = str.Replace(m, ""));

4

Una stringa è solo una matrice di caratteri, quindi usa Linq per fare il rimpiazzo (simile ad Albin sopra tranne per il fatto che usa un linq contiene un'istruzione per fare il rimpiazzo):

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

La prima stringa è la stringa in cui sostituire i caratteri e la seconda è una stringa semplice contenente i caratteri


La soluzione Linq di Albin è probabilmente migliore, a meno che non ci siano caratteri aggiuntivi che desideri filtrare (non coperti da spazi bianchi, lettere e cifre).
alistair,

3

Potrei anche buttarlo qui.

Crea un'estensione per rimuovere i caratteri da una stringa:

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();
}

Ed è utilizzabile in questo modo:

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

O proprio così:

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

Questa è la soluzione migliore, in quanto rende il minor numero di allocazioni di memoria. Vorrei anche impostare la lunghezza della stringa originale come capacità iniziale del generatore di stringhe, come: new StringBuilder (input.Length) a questo scopo di avere il minor numero di allocazioni di memoria.
tesoro

3

Sembra che il modo più breve sia combinare LINQ e 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" 

Guarda la demo di C # . Si noti che string.Concatè una scorciatoia per string.Join("", ...).

Si noti che l'utilizzo di un regex per rimuovere singoli caratteri noti è ancora possibile costruire in modo dinamico, anche se si ritiene che regex sia più lento. Tuttavia, ecco un modo per costruire una regex così dinamica (dove tutto ciò che serve è una classe di caratteri):

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

Guarda un'altra demo di C # . L'espressione regolare sarà simile [@,\.;']+(una corrispondenza o più ( +) occorrenze consecutive di @, ,, ., ;o 'caratteri) dove il punto non ha per essere sfuggito, ma Regex.Escapesarà necessario per sfuggire altri caratteri che devono essere sfuggiti, come \, ^, ]o -la cui posizione all'interno della classe del personaggio non puoi prevederlo.


la via linq è terribilmente lenta in alcuni casi .
drzaus,

3

Ecco un metodo che ho scritto che ha un approccio leggermente diverso. Invece di specificare i caratteri da rimuovere, dico al mio metodo quali caratteri voglio mantenere: rimuoveranno tutti gli altri caratteri.

Nell'esempio del PO, vuole solo mantenere caratteri e spazi alfabetici. Ecco come sarebbe una chiamata al mio metodo ( demo C # ):

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

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

Ecco il mio metodo:

/// <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;
}

Risposta fantastica!
edtheprogrammerguy,

Molto bella! la stringa numerica ha 0 due volte.
John Kurtz,

@JohnKurtz Bella cattura - ora non c'è più.
Mass Dot Net

2

Molte buone risposte qui, ecco la mia aggiunta insieme a diversi test unitari che possono essere usati per aiutare a testare la correttezza, la mia soluzione è simile a quella di @ Rianne sopra ma usa un ISet per fornire O (1) tempo di ricerca sui caratteri sostitutivi (e anche simile alla soluzione Linq di @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());
    }

Test NUnit (2.6+) qui

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]);
    }
}

2

È un metodo potente che di solito uso nello stesso caso:

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

Godere...


1

Old School sul posto copia / stomp:

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

Non sono sicuro dell'efficienza rispetto ad altri metodi (ovvero il sovraccarico di tutte le chiamate di funzione e le istanze che si verificano come effetto collaterale nell'esecuzione di C #).


1

Lo faccio metodo di estensione e con array di stringhe, penso che string[]sia più utile di char[]perché char può anche essere stringa:

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

allora puoi usarlo ovunque:

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

1

Avevo bisogno di rimuovere caratteri speciali da un file XML. Ecco come l'ho fatto. char.ToString () è l'eroe in questo codice.

string item = "<item type="line" />"
char DC4 = (char)0x14;
string fixed = item.Replace(DC4.ToString(), string.Empty);

1
new[] { ',', '.', ';', '\'', '@' }
.Aggregate("My name @is ,Wan.;'; Wan", (s, c) => s.Replace(c.ToString(), string.Empty)); 

1

Prendendo le cifre delle prestazioni da @drzaus, ecco un metodo di estensione che utilizza l'algoritmo più veloce.

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.