Supprimer les caractères de la chaîne C #

150

Comment puis-je supprimer des caractères d'une chaîne? Par exemple: "My name @is ,Wan.;'; Wan".

Je voudrais supprimer les caractères '@', ',', '.', ';', '\''de cette chaîne pour qu'elle devienne"My name is Wan Wan"

Wanwan
la source

Réponses:

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

Mais je peux suggérer une autre approche si vous souhaitez supprimer tous les caractères non alphabétiques

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
la source
12
Peut également être fait comme ceci, str = new string (str.Where (x => char.IsWhiteSpace (x) || char.IsLetterOrDigit (x)). ToArray ());
Adnan Bhatti le
1
J'ai dû chercher ceci, string.Empty ne crée pas de chaîne pour la comparaison donc c'est plus efficace que "". ( stackoverflow.com/questions/151472/… )
Tom Cerul
6
Suis-je le seul à avoir "Argument 2: impossible de convertir de 'string' en 'char'" om string.Empty?
OddDev
2
@OddDev vous ne devriez obtenir cette erreur que si votre tableau que vous parcourez en boucle est une liste de caractères. Si ce sont des chaînes, cela devrait fonctionner
Newteq Developer
3
Notez également que pour que la fonction "str.Replace" fonctionne correctement, le premier paramètre doit être une "chaîne" si vous souhaitez utiliser string.Empty comme deuxième paramètre. Si vous utilisez un caractère (c'est-à-dire «a») comme premier paramètre, vous aurez également besoin d'un caractère comme second. Sinon, vous obtiendrez l'erreur «Argument 2: impossible de convertir de 'chaîne' en 'char'» »mentionnée par @OddDev ci
Leo
68

Facile:

String.Join("", "My name @is ,Wan.;'; Wan".Split('@', ',' ,'.' ,';', '\''));
Énigmativité
la source
64

Sonne comme une application idéale pour RegEx - un moteur conçu pour une manipulation rapide de texte. Dans ce cas:

Regex.Replace("He\"ll,o Wo'r.ld", "[@,\\.\";'\\\\]", string.Empty)
John Melville
la source
3
Il semble que ce serait beaucoup plus efficace qu'une approche basée sur un itérateur, surtout si vous pouvez utiliser un Regex compilé;
Ade Miller
Cela devrait être la réponse acceptée, d'autant plus que, comme l'a dit @AdeMiller, elle sera beaucoup plus efficace.
Obsidian
14
Ce n'est pas plus rapide que la boucle, c'est une idée fausse courante selon laquelle les regex sont toujours plus rapides que les boucles. Les expressions régulières ne sont pas magiques, elles doivent à un moment donné parcourir la chaîne pour effectuer leurs opérations, et elles peuvent être beaucoup plus lentes avec les frais généraux de l'expression régulière elle-même. Ils excellent vraiment lorsqu'il s'agit de manipulations extrêmement complexes, où des dizaines de lignes de code et de multiples boucles seraient nécessaires. En testant la version compilée de ce regex contre une simple boucle non optimisée 50000 fois, le regex est 6X plus lent.
Tony Cheetham
Qu'en est-il de l'efficacité de la mémoire? Les expressions régulières ne seront-elles pas plus efficaces dans le sens des nouvelles allocations de chaînes?
Marek
2
Peut-être que je me suis mal exprimé en affirmant que RegEx est rapide. À moins que cela ne soit au centre d'une boucle très serrée, d'autres considérations, une telle lisibilité et maintenabilité sont susceptibles de dominer les performances pour une petite opération comme celle-ci.
John Melville
21

Moins spécifique à votre question, il est possible de supprimer TOUTES les signes de ponctuation d'une chaîne (sauf l'espace) en listant en blanc les caractères acceptables dans une expression régulière:

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

Notez qu'il y a un espace après ce 9 pour ne pas supprimer les espaces de votre phrase. Le troisième argument est une chaîne vide qui sert à remplacer toute sous-chaîne n'appartenant pas à l'expression régulière.

ThisClark
la source
19

Comparer diverses suggestions (ainsi que comparer dans le cadre de remplacements à un seul caractère avec différentes tailles et positions de la cible).

Dans ce cas particulier, le fractionnement sur les cibles et la jonction sur les remplacements (dans ce cas, une chaîne vide) est le plus rapide d'au moins un facteur de 3. En fin de compte, les performances sont différentes en fonction du nombre de remplacements, où les remplaçants sont en la source et la taille de la source. #ymmv

Résultats

(résultats complets ici )

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

Faisceau de test (LinqPad)

(note: les extensions de synchronisationPerf et Vssont que j'ai écrites )

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
la source
1
Enfin quelques chiffres! Bon travail @drzaus!
Marek
17
 string x = "My name @is ,Wan.;'; Wan";
 string modifiedString = x.Replace("@", "").Replace(",", "").Replace(".", "").Replace(";", "").Replace("'", "");
Mostafa
la source
Cela ne fonctionnera pas car string.Replace renvoie une "chaîne modifiée". Voir stackoverflow.com/a/13277669/6198927
Esteban Verbel
8

Le moyen le plus simple serait d'utiliser String.Replace:

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

Une autre solution 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
la source
5
new List<string> { "@", ",", ".", ";", "'" }.ForEach(m => str = str.Replace(m, ""));
MirlvsMaximvs
la source
4

Une chaîne est juste un tableau de caractères, utilisez donc Linq pour effectuer le remplacement (similaire à Albin ci-dessus sauf utilise une instruction linq contains pour effectuer le remplacement):

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

La première chaîne est la chaîne dans laquelle remplacer les caractères et la seconde est une chaîne simple contenant les caractères

alistair
la source
La solution Linq d'Albin est probablement meilleure, sauf s'il y a des caractères supplémentaires que vous souhaitez filtrer (non couverts par des espaces, des lettres et des chiffres).
alistair le
3

Je pourrais aussi bien jeter ceci ici.

Créez une extension pour supprimer les caractères d'une chaîne:

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

Et c'est utilisable comme ça:

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

Ou juste comme ça:

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

la source
C'est la meilleure solution, car elle fait le plus petit nombre d'allocations de mémoire. Je définirais également la longueur de la chaîne d'origine comme la capacité initiale du générateur de chaînes, comme: new StringBuilder (input.Length) dans ce but d'avoir le moins d'allocations de mémoire.
Treasurychf
3

Il semble que le moyen le plus court soit de combiner LINQ et 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" 

Voir la démo C # . Notez qu'il string.Concats'agit d'un raccourci versstring.Join("", ...) .

Notez que l'utilisation d'une expression régulière pour supprimer des caractères connus individuels est toujours possible de construire dynamiquement, bien que l'on pense que l'expression régulière est plus lente. Cependant, voici un moyen de créer une telle regex dynamique (où tout ce dont vous avez besoin est une classe de caractères):

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

Voir une autre démo C # . Le regex ressemblera [@,\.;']+(une correspondance ou plus ( +) occurrences consécutives @, ,, ., ;ou 'caractères) où le point n'a pas à être échappé, mais Regex.Escapesera nécessaire pour échapper à d' autres caractères qui doivent être échappées, comme \, ^, ]ou -dont la position à l'intérieur de la classe de caractères, vous ne pouvez pas prédire.

Wiktor Stribiżew
la source
la manière linq est affreusement lente dans certains cas .
drzaus
3

Voici une méthode que j'ai écrite qui adopte une approche légèrement différente. Plutôt que de spécifier les caractères à supprimer, j'indique à ma méthode quels caractères je veux conserver - cela supprimera tous les autres caractères.

Dans l'exemple de l'OP, il souhaite uniquement conserver les caractères alphabétiques et les espaces. Voici à quoi ressemblerait un appel à ma méthode ( démo C # ):

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

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

Voici ma méthode:

/// <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;
}
Réseau de points de masse
la source
Réponse géniale!
edtheprogrammerguy
Très agréable! la chaîne numérique a deux fois 0.
John Kurtz le
@JohnKurtz Belle prise - c'est parti maintenant.
Mass Dot Net
2

Beaucoup de bonnes réponses ici, voici mon ajout ainsi que plusieurs tests unitaires qui peuvent être utilisés pour aider à tester l'exactitude, ma solution est similaire à celle de @ Rianne ci-dessus mais utilise un ISet pour fournir un temps de recherche O (1) sur les caractères de remplacement (et aussi similaire à la solution 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());
    }

Tests NUnit (2.6+) ici

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
la source
2

C'est une méthode puissante que j'utilise habituellement dans le même cas:

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

Prendre plaisir...

Mohammad Fathi MiMFa
la source
1

Old School en place copie / 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));
     }

Pas sûr de l'efficacité avec les autres méthodes (c'est-à-dire la surcharge de tous les appels de fonction et instanciations qui se produisent comme un effet secondaire dans l'exécution C #).

user6262837
la source
1

Je fais une méthode d'extension et avec un tableau de chaînes, je pense que string[]c'est plus utile que char[]parce que char peut également être une chaîne:

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

alors vous pouvez l'utiliser n'importe où:

string myname = "My name @is ,Wan.;'; Wan";
string result = myname.RemoveStrs(new[]{ "@", ",", ".", ";", "\\"});
yu yang jian
la source
1

J'avais besoin de supprimer les caractères spéciaux d'un fichier XML. Voici comment je l'ai fait. char.ToString () est le héros de ce code.

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

En prenant les chiffres de performances de @drzaus, voici une méthode d'extension qui utilise l'algorithme le plus rapide.

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

Usage

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