Entfernen Sie Zeichen aus der C # -String

150

Wie kann ich Zeichen aus einer Zeichenfolge entfernen? Zum Beispiel : "My name @is ,Wan.;'; Wan".

Ich möchte die Zeichen '@', ',', '.', ';', '\''aus dieser Zeichenfolge entfernen , damit es wird"My name is Wan Wan"

Wan wan
quelle

Antworten:

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

Ich kann jedoch einen anderen Ansatz vorschlagen, wenn Sie alle Nicht-Buchstaben-Zeichen entfernen möchten

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
quelle
12
Dies kann auch folgendermaßen erfolgen: str = new string (str.Where (x => char.IsWhiteSpace (x) || char.IsLetterOrDigit (x)). ToArray ());
Adnan Bhatti
1
Ich musste das nachschlagen, string.Empty erstellt keinen String für den Vergleich, daher ist es effizienter als "". ( stackoverflow.com/questions/151472/… )
Tom Cerul
6
Bin ich der einzige, der "Argument 2: Kann nicht von 'string' nach 'char' konvertieren" om string.Empty erhält?
OddDev
2
@OddDev Sie sollten diesen Fehler nur erhalten, wenn Ihr Array, das Sie durchlaufen, eine Liste von Zeichen ist. Wenn es sich um Zeichenfolgen handelt, sollte dies funktionieren
Newteq Developer
3
Beachten Sie außerdem, dass der erste Parameter ein "String" sein muss, damit die Funktion "str.Replace" ordnungsgemäß funktioniert, wenn Sie string.Empty als zweiten Parameter verwenden möchten. Wenn Sie ein Zeichen (dh 'a') als ersten Parameter verwenden, benötigen Sie auch ein Zeichen als zweiten Parameter. Andernfalls wird der von @OddDev oben erwähnte Fehler "Argument 2: Konvertieren von 'Zeichenfolge' in 'Zeichen'" nicht angezeigt
Leo
68

Einfach:

String.Join("", "My name @is ,Wan.;'; Wan".Split('@', ',' ,'.' ,';', '\''));
Rätselhaftigkeit
quelle
64

Klingt nach einer idealen Anwendung für RegEx - eine Engine für die schnelle Textmanipulation. In diesem Fall:

Regex.Replace("He\"ll,o Wo'r.ld", "[@,\\.\";'\\\\]", string.Empty)
John Melville
quelle
3
Dies scheint weitaus effizienter zu sein als ein iteratorbasierter Ansatz, insbesondere wenn Sie einen kompilierten Regex verwenden können.
Ade Miller
Dies sollte die akzeptierte Antwort sein, insbesondere weil sie, wie @AdeMiller sagte, weitaus effizienter sein wird.
Obsidian
14
Dies ist nicht schneller als die Schleife. Es ist ein weit verbreitetes Missverständnis, dass Regex immer schneller als Schleifen sind. Regex ist keine Magie, im Kern müssen sie irgendwann durch die Zeichenfolge iterieren, um ihre Operationen auszuführen, und sie können mit dem Overhead des Regex selbst viel langsamer sein. Sie zeichnen sich wirklich durch äußerst komplexe Manipulationen aus, bei denen Dutzende von Codezeilen und mehrere Schleifen erforderlich wären. Wenn Sie die kompilierte Version dieses regulären Ausdrucks 50000 Mal gegen eine einfache, nicht optimierte Schleife testen, ist der reguläre Ausdruck 6-mal langsamer.
Tony Cheetham
Was ist mit Speichereffizienz? Werden reguläre Ausdrücke im Hinblick auf die Zuweisung neuer Zeichenfolgen nicht effizienter sein?
Marek
2
Vielleicht habe ich falsch geschrieben, als ich behauptete, dass RegEx schnell ist. Wenn dies nicht im Zentrum einer sehr engen Schleife steht, dominieren wahrscheinlich andere Überlegungen, wie Lesbarkeit und Wartbarkeit, die Leistung für einen kleinen Vorgang wie diesen.
John Melville
21

Weniger spezifisch für Ihre Frage ist es möglich, ALLE Satzzeichen aus einer Zeichenfolge (außer Leerzeichen) zu entfernen, indem Sie die zulässigen Zeichen in einem regulären Ausdruck weiß auflisten:

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

Beachten Sie, dass nach 9 ein Leerzeichen steht, um keine Leerzeichen aus Ihrem Satz zu entfernen. Das dritte Argument ist eine leere Zeichenfolge, die dazu dient, alle Teilzeichenfolgen zu ersetzen, die nicht zum regulären Ausdruck gehören.

ThisClark
quelle
19

Vergleich verschiedener Vorschläge (sowie Vergleich im Zusammenhang mit Einzelzeichenersetzungen mit verschiedenen Größen und Positionen des Ziels).

In diesem speziellen Fall ist das Aufteilen auf die Ziele und das Verbinden der Ersetzungen (in diesem Fall die leere Zeichenfolge) um mindestens den Faktor 3 am schnellsten. Letztendlich hängt die Leistung von der Anzahl der Ersetzungen ab, in denen sich die Ersetzungen befinden die Quelle und die Größe der Quelle. #ymmv

Ergebnisse

(vollständige Ergebnisse hier )

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

Testkabelbaum (LinqPad)

(Anmerkung: die Perfund Vssind Timing-Erweiterungen, die ich geschrieben habe )

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
quelle
1
Endlich ein paar Zahlen! Gute Arbeit @drzaus!
Marek
17
 string x = "My name @is ,Wan.;'; Wan";
 string modifiedString = x.Replace("@", "").Replace(",", "").Replace(".", "").Replace(";", "").Replace("'", "");
mostafa
quelle
Dies funktioniert nicht, da string.Replace einen "geänderten String" zurückgibt. Siehe stackoverflow.com/a/13277669/6198927
Esteban Verbel
8

Der einfachste Weg wäre zu verwenden String.Replace:

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

Eine weitere einfache Lösung:

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

Eine Zeichenfolge ist nur ein Zeichenarray. Verwenden Sie also Linq, um das Ersetzen durchzuführen (ähnlich wie bei Albin oben, außer dass eine linq enthält-Anweisung zum Ersetzen verwendet wird):

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

Die erste Zeichenfolge ist die Zeichenfolge, in der Zeichen ersetzt werden sollen, und die zweite Zeichenfolge ist eine einfache Zeichenfolge, die die Zeichen enthält

Alistair
quelle
Albins Linq-Lösung ist wahrscheinlich besser, es sei denn, Sie möchten zusätzliche Zeichen herausfiltern (nicht durch Leerzeichen, Buchstaben und Ziffern abgedeckt).
Alistair
3

Ich könnte das genauso gut hier rauswerfen.

Erstellen Sie eine Erweiterung, um Zeichen aus einer Zeichenfolge zu entfernen:

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

Und es ist so verwendbar:

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

Oder einfach so:

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

quelle
Dies ist die beste Lösung, da nur die geringste Anzahl von Speicherzuweisungen vorgenommen wird. Ich würde auch die Länge der ursprünglichen Zeichenfolge als Anfangskapazität des Zeichenfolgengenerators festlegen, z. B.: New StringBuilder (input.Length), um die geringste Anzahl von Speicherzuordnungen zu erhalten.
Treaschf
3

Es scheint, dass der kürzeste Weg darin besteht, LINQ zu kombinieren und 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" 

Siehe die C # -Demo . Beachten Sie, dass dies string.Concateine Verknüpfung zu ist string.Join("", ...).

Beachten Sie, dass die Verwendung eines regulären Ausdrucks zum Entfernen einzelner bekannter Zeichen weiterhin dynamisch erstellt werden kann, obwohl angenommen wird, dass der reguläre Ausdruck langsamer ist. Hier ist jedoch eine Möglichkeit, einen solchen dynamischen regulären Ausdruck zu erstellen (wobei Sie lediglich eine Zeichenklasse benötigen):

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

Sehen Sie sich eine andere C # -Demo an . Die Regex wird wie folgt aussehen [@,\.;']+(Matching ein oder mehr ( +) aufeinander folgende Vorkommen @, ,, ., ;oder 'Zeichen) , wo der Punkt nicht entgangen sein muss, aber Regex.Escapewird notwendig sein , andere Zeichen zu entkommen , die maskiert werden müssen, wie \, ^, ]oder -deren Position innerhalb der Zeichenklasse können Sie nicht vorhersagen.

Wiktor Stribiżew
quelle
Der Linq-Weg ist in einigen Fällen schrecklich langsam .
Drzaus
3

Hier ist eine Methode, die ich geschrieben habe und die einen etwas anderen Ansatz verfolgt. Anstatt die zu entfernenden Zeichen anzugeben, sage ich meiner Methode, welche Zeichen ich behalten möchte - alle anderen Zeichen werden entfernt.

Im Beispiel des OP möchte er nur alphabetische Zeichen und Leerzeichen behalten. So würde ein Aufruf meiner Methode aussehen ( C # -Demo ):

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

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

Hier ist meine Methode:

/// <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
quelle
Super Antwort!
edtheprogrammerguy
Sehr schön! Die numerische Zeichenfolge hat zweimal 0.
John Kurtz
@ JohnKurtz Netter Fang - es ist jetzt weg.
Mass Dot Net
2

Viele gute Antworten hier, hier ist meine Ergänzung zusammen mit mehreren Komponententests, die zum Testen der Korrektheit verwendet werden können. Meine Lösung ähnelt der von @ Rianne oben, verwendet jedoch ein ISet, um die O (1) -Suchzeit für die Ersatzzeichen (und auch) bereitzustellen ähnlich der Linq-Lösung von @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());
    }

NUnit (2.6+) testet hier

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
quelle
2

Es ist eine leistungsstarke Methode, die ich normalerweise im selben Fall verwende:

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

Genießen...

Mohammad Fathi MiMFa
quelle
1

Old School an Ort und Stelle kopieren / stampfen:

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

Sie sind sich nicht sicher über die Effizienz anderer Methoden (dh den Overhead aller Funktionsaufrufe und Instanziierungen, die als Nebeneffekt bei der C # -Ausführung auftreten).

user6262837
quelle
1

Ich mache es Erweiterungsmethode und mit String-Array denke ich, string[]ist nützlicher als char[]weil char auch String sein kann:

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

dann können Sie es überall verwenden:

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

Ich musste Sonderzeichen aus einer XML-Datei entfernen. So habe ich es gemacht. char.ToString () ist der Held in diesem Code.

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

Ausgehend von den Leistungsdaten von @drzaus finden Sie hier eine Erweiterungsmethode, die den schnellsten Algorithmus verwendet.

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

Verwendung

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