Die effizienteste Methode zum Entfernen von Sonderzeichen aus Zeichenfolgen

266

Ich möchte alle Sonderzeichen aus einer Zeichenfolge entfernen. Zulässige Zeichen sind AZ (Groß- oder Kleinbuchstaben), Zahlen (0-9), Unterstrich (_) oder das Punktzeichen (.).

Ich habe folgendes, es funktioniert, aber ich vermute (ich weiß!), Es ist nicht sehr effizient:

    public static string RemoveSpecialCharacters(string str)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.Length; i++)
        {
            if ((str[i] >= '0' && str[i] <= '9')
                || (str[i] >= 'A' && str[i] <= 'z'
                    || (str[i] == '.' || str[i] == '_')))
                {
                    sb.Append(str[i]);
                }
        }

        return sb.ToString();
    }

Was ist der effizienteste Weg, dies zu tun? Wie würde ein regulärer Ausdruck aussehen und wie ist er mit einer normalen String-Manipulation zu vergleichen?

Die zu bereinigenden Zeichenfolgen sind ziemlich kurz und normalerweise zwischen 10 und 30 Zeichen lang.

Obi Wan Kenobi
quelle
5
Ich werde dies nicht in eine Antwort aufnehmen, da es nicht effizienter sein wird, aber es gibt eine Reihe von statischen char-Methoden wie char.IsLetterOrDigit (), die Sie in Ihrer if-Anweisung verwenden könnten, um es zumindest lesbarer zu machen.
Martin Harris
5
Ich bin mir nicht sicher, ob das Überprüfen auf A bis Z sicher ist, da es 6 Zeichen enthält, die nicht alphabetisch sind, von denen nur eines erwünscht ist (Unterleiste).
Steven Sudit
4
Konzentrieren Sie sich darauf, Ihren Code besser lesbar zu machen. Wenn Sie dies nicht in einer Schleife wie 500 Mal pro Sekunde tun, ist die Effizienz keine große Sache. Verwenden Sie einen regulären Ausdruck und es wird viel einfacher zu lesen sein.l
Byron Whitlock
4
Byron, Sie haben wahrscheinlich Recht damit, die Lesbarkeit zu betonen. Ich bin jedoch skeptisch, ob regulärer Ausdruck lesbar ist. :-)
Steven Sudit
2
Reguläre Ausdrücke, die lesbar sind oder nicht, sind vergleichbar damit, ob Deutsch lesbar ist oder nicht. es hängt davon ab, ob Sie es wissen oder nicht (obwohl Sie in beiden Fällen hin und wieder auf grammatikalische Regeln stoßen, die keinen Sinn ergeben;)
Blixt

Antworten:

325

Warum ist Ihre Methode Ihrer Meinung nach nicht effizient? Es ist tatsächlich eine der effizientesten Möglichkeiten, dies zu tun.

Sie sollten das Zeichen natürlich in eine lokale Variable einlesen oder einen Enumerator verwenden, um die Anzahl der Array-Zugriffe zu verringern:

public static string RemoveSpecialCharacters(this string str) {
   StringBuilder sb = new StringBuilder();
   foreach (char c in str) {
      if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '.' || c == '_') {
         sb.Append(c);
      }
   }
   return sb.ToString();
}

Eine Sache, die eine solche Methode effizient macht, ist, dass sie gut skaliert. Die Ausführungszeit ist relativ zur Länge der Zeichenfolge. Es gibt keine bösen Überraschungen, wenn Sie es auf einer großen Saite verwenden würden.

Bearbeiten:
Ich habe einen schnellen Leistungstest durchgeführt und jede Funktion millionenfach mit einer 24-stelligen Zeichenfolge ausgeführt. Dies sind die Ergebnisse:

Ursprüngliche Funktion: 54,5 ms.
Meine vorgeschlagene Änderung: 47,1 ms.
Meins mit eingestellter StringBuilder-Kapazität: 43,3 ms.
Regulärer Ausdruck: 294,4 ms.

Edit 2: Ich habe im obigen Code die Unterscheidung zwischen AZ und az hinzugefügt. (Ich habe den Leistungstest erneut durchgeführt, und es gibt keinen erkennbaren Unterschied.)

Edit 3:
Ich habe die Lookup + Char [] -Lösung getestet und sie läuft in ca. 13 ms.

Der zu zahlende Preis ist natürlich die Initialisierung der riesigen Nachschlagetabelle und deren Speicherung. Nun, es sind nicht so viele Daten, aber es ist viel für eine so triviale Funktion ...

private static bool[] _lookup;

static Program() {
   _lookup = new bool[65536];
   for (char c = '0'; c <= '9'; c++) _lookup[c] = true;
   for (char c = 'A'; c <= 'Z'; c++) _lookup[c] = true;
   for (char c = 'a'; c <= 'z'; c++) _lookup[c] = true;
   _lookup['.'] = true;
   _lookup['_'] = true;
}

public static string RemoveSpecialCharacters(string str) {
   char[] buffer = new char[str.Length];
   int index = 0;
   foreach (char c in str) {
      if (_lookup[c]) {
         buffer[index] = c;
         index++;
      }
   }
   return new string(buffer, 0, index);
}
Guffa
quelle
4
Genau. Die einzige andere Änderung, die ich vornehmen würde, ist das Hinzufügen des anfänglichen Kapazitätsarguments zum StringBuilder-Konstruktor "= new StringBuilder (str.Length)".
David
2
Meine Antwort, die char[]eher einen Puffer als einen verwendet StringBuilder, hat laut meinen Tests einen leichten Vorteil gegenüber diesem. (Mine ist jedoch weniger lesbar, so dass sich der kleine Leistungsvorteil wahrscheinlich nicht lohnt.)
LukeH
1
@Steven: Das mag durchaus der Fall sein, aber die Benchmarks sprechen für sich! In meinen Tests ist die Verwendung eines char[]Puffers (geringfügig) besser als StringBuilderbei der Skalierung auf Zeichenfolgen mit einer Länge von Zehntausenden von Zeichen.
LukeH
10
@downvoter: Warum das Downvote? Wenn Sie nicht erklären, was Sie für falsch halten, kann dies die Antwort nicht verbessern.
Guffa
2
@SILENT: Nein, das tut es nicht, aber du solltest das nur einmal tun. Wenn Sie bei jedem Aufruf der Methode ein so großes Array zuweisen (und wenn Sie die Methode häufig aufrufen), wird die Methode bei weitem die langsamste und verursacht viel Arbeit für den Garbage Collector.
Guffa
195

Nun, es sei denn, Sie müssen die Leistung wirklich aus Ihrer Funktion herausholen, wählen Sie einfach das, was am einfachsten zu warten und zu verstehen ist. Ein regulärer Ausdruck würde folgendermaßen aussehen:

Für zusätzliche Leistung können Sie es entweder vorkompilieren oder einfach beim ersten Aufruf kompilieren (nachfolgende Aufrufe sind schneller).

public static string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}
Blixt
quelle
1
Ich würde vermuten, dass dies wahrscheinlich eine so komplexe Abfrage ist, dass sie schneller als der Ansatz des OP wäre, insbesondere wenn sie vorkompiliert wäre. Ich habe jedoch keine Beweise dafür. Es sollte getestet werden. Wenn es nicht drastisch langsamer ist, würde ich diesen Ansatz trotzdem wählen, da er viel einfacher zu lesen und zu warten ist. +1
rmeador
6
Es ist eine sehr einfache Regex (kein Backtracking oder komplexes Zeug), also sollte es verdammt schnell sein.
9
@rmeador: Ohne Kompilierung ist es ungefähr 5x langsamer, kompiliert ist es 3x langsamer als seine Methode. Immer noch 10x einfacher :-D
user7116
6
Reguläre Ausdrücke sind keine magischen Hämmer und niemals schneller als handoptimierter Code.
Christian Klauser
2
Für diejenigen, die sich an Knuths berühmtes Zitat über Optimierung erinnern, ist dies der Ausgangspunkt. Wenn Sie dann feststellen, dass Sie die zusätzliche Tausendstel-Millisekunden-Leistung benötigen, wählen Sie eine der anderen Techniken.
John
15

Ich schlage vor, eine einfache Nachschlagetabelle zu erstellen, die Sie im statischen Konstruktor initialisieren können, um eine beliebige Zeichenkombination auf gültig zu setzen. Auf diese Weise können Sie eine schnelle Einzelprüfung durchführen.

bearbeiten

Aus Gründen der Geschwindigkeit sollten Sie auch die Kapazität Ihres StringBuilder auf die Länge Ihrer Eingabezeichenfolge initialisieren. Dadurch werden Neuzuweisungen vermieden. Diese beiden Methoden zusammen bieten Ihnen Geschwindigkeit und Flexibilität.

eine andere Bearbeitung

Ich denke, der Compiler könnte es optimieren, aber aus Gründen des Stils und der Effizienz empfehle ich foreach anstelle von for.

Steven Sudit
quelle
Für Arrays forund foreacherzeugen Sie ähnlichen Code. Ich weiß allerdings nichts über Saiten. Ich bezweifle, dass die JIT die Array-ähnliche Natur von String kennt.
Christian Klauser
1
Ich wette, die JIT weiß mehr über die Array-ähnliche Natur von Strings als Ihr [Witz entfernt]. Anders
Ich habe dies mit HashSet <char> gemacht und es ist ungefähr 2x langsamer als seine Methode. Die Verwendung von bool [] ist kaum schneller (0,0469 ms / iter v. 0,0559 ms / iter) als die Version, die er in OP hat ... mit dem Problem, weniger lesbar zu sein.
user7116
1
Ich konnte keinen Leistungsunterschied zwischen der Verwendung eines Bool-Arrays und eines Int-Arrays feststellen. Ich würde ein Bool-Array verwenden, da es die Nachschlagetabelle von 256 KB auf 64 KB reduziert, aber es sind immer noch viele Daten für eine so triviale Funktion ... Und es ist nur etwa 30% schneller.
Guffa
1
@Guffa 2) Da wir nur alphanumerische Zeichen und einige lateinische Grundzeichen beibehalten, benötigen wir nur eine Tabelle für das niedrige Byte, sodass die Größe kein wirkliches Problem darstellt. Wenn wir universell sein wollen, dann ist die Standard-Unicode-Technik die doppelte Indirektion. Mit anderen Worten, eine Tabelle mit 256 Tabellenreferenzen, von denen viele auf dieselbe leere Tabelle verweisen.
Steven Sudit
12
public static string RemoveSpecialCharacters(string str)
{
    char[] buffer = new char[str.Length];
    int idx = 0;

    foreach (char c in str)
    {
        if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
            || (c >= 'a' && c <= 'z') || (c == '.') || (c == '_'))
        {
            buffer[idx] = c;
            idx++;
        }
    }

    return new string(buffer, 0, idx);
}
LukeH
quelle
1
+1, getestet und ungefähr 40% schneller als StringBuilder. 0,0294 ms / Zeichenfolge v. 0,0399 ms / Zeichenfolge
user7116
Meinen Sie StringBuilder mit oder ohne Vorbelegung?
Steven Sudit
Mit der Vorzuweisung ist es immer noch 40% langsamer als die Zuweisung von char [] und der neuen Zeichenfolge.
user7116
2
Ich mag das. Ich habe diese Methode optimiertforeach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Chris Marisic
11

Ein regulärer Ausdruck sieht folgendermaßen aus:

public string RemoveSpecialChars(string input)
{
    return Regex.Replace(input, @"[^0-9a-zA-Z\._]", string.Empty);
}

Wenn die Leistung jedoch sehr wichtig ist, empfehle ich Ihnen, einige Benchmarks durchzuführen, bevor Sie den "Regex-Pfad" auswählen ...

CMS
quelle
11

Wenn Sie eine dynamische Liste von Zeichen verwenden, bietet LINQ möglicherweise eine viel schnellere und elegantere Lösung:

public static string RemoveSpecialCharacters(string value, char[] specialCharacters)
{
    return new String(value.Except(specialCharacters).ToArray());
}

Ich habe diesen Ansatz mit zwei der vorherigen "schnellen" Ansätze verglichen (Release-Kompilierung):

  • Char Array Lösung von LukeH - 427 ms
  • StringBuilder-Lösung - 429 ms
  • LINQ (diese Antwort) - 98 ms

Beachten Sie, dass der Algorithmus leicht modifiziert ist - die Zeichen werden als Array übergeben und nicht fest codiert, was sich geringfügig auf die Dinge auswirken könnte (dh die anderen Lösungen hätten eine innere Schleife, um das Zeichenarray zu überprüfen).

Wenn ich mit einer LINQ where-Klausel zu einer fest codierten Lösung wechsle, sind die Ergebnisse:

  • Char Array Lösung - 7ms
  • StringBuilder-Lösung - 22ms
  • LINQ - 60 ms

Es könnte sich lohnen, sich LINQ oder einen modifizierten Ansatz anzusehen, wenn Sie eine allgemeinere Lösung schreiben möchten, anstatt die Liste der Zeichen fest zu codieren. LINQ bietet Ihnen definitiv präzisen, gut lesbaren Code - noch mehr als Regex.

Schattenjäger
quelle
3
Dieser Ansatz sieht gut aus, funktioniert aber nicht - Außer () ist eine festgelegte Operation, sodass nur das erste Auftreten jedes einzelnen Zeichens in der Zeichenfolge angezeigt wird.
McKenzieG1
5

Ich bin nicht davon überzeugt, dass Ihr Algorithmus alles andere als effizient ist. Es ist O (n) und betrachtet jedes Zeichen nur einmal. Besser geht es nicht, wenn Sie die Werte nicht auf magische Weise kennen, bevor Sie sie überprüfen.

Ich würde jedoch die Kapazität von Ihnen initialisieren StringBuilder auf die anfängliche Größe der Zeichenfolge . Ich vermute, dass Ihr wahrgenommenes Leistungsproblem auf die Neuzuweisung von Speicher zurückzuführen ist.

Randnotiz: Überprüfen A - zist nicht sicher. Sie sind einschließlich [, \, ], ^,_ , und `...

Randnotiz 2: Um die Effizienz zu steigern, ordnen Sie die Vergleiche so an, dass die Anzahl der Vergleiche minimiert wird. (Im schlimmsten Fall sprechen Sie von 8 Vergleichen, denken Sie also nicht zu viel nach.) Dies ändert sich mit Ihrer erwarteten Eingabe, aber ein Beispiel könnte sein:

if (str[i] >= '0' && str[i] <= 'z' && 
    (str[i] >= 'a' || str[i] <= '9' ||  (str[i] >= 'A' && str[i] <= 'Z') || 
    str[i] == '_') || str[i] == '.')

Randnotiz 3: Wenn Sie dies aus irgendeinem Grund WIRKLICH benötigen, um schnell zu sein, kann eine switch-Anweisung schneller sein. Der Compiler sollte eine Sprungtabelle für Sie erstellen, die nur einen einzigen Vergleich ergibt:

switch (str[i])
{
    case '0':
    case '1':
    .
    .
    .
    case '.':
        sb.Append(str[i]);
        break;
}
lc.
quelle
1
Ich bin damit einverstanden, dass Sie O (n) in diesem Fall nicht schlagen können. Es gibt jedoch Kosten pro Vergleich, die gesenkt werden können. Eine Tabellensuche hat niedrige Fixkosten, während eine Reihe von Vergleichen die Kosten erhöhen wird, wenn Sie weitere Ausnahmen hinzufügen.
Steven Sudit
Glauben Sie wirklich, dass die Sprungtabelle in Bezug auf Randnotiz 3 schneller ist als die Tabellensuche?
Steven Sudit
Ich habe den schnellen Leistungstest für die Switch-Lösung durchgeführt und er funktioniert genauso wie der Vergleich.
Guffa
@Steven Sudit - Ich würde es wagen, dass sie tatsächlich ungefähr gleich sind. Möchtest du einen Test durchführen?
lc.
7
O (n) Notation macht mich manchmal wütend. Die Leute werden dumme Annahmen treffen, basierend auf der Tatsache, dass der Algorithmus bereits O (n) ist. Wenn wir diese Routine ändern würden, um die str [i] -Aufrufe durch eine Funktion zu ersetzen, die den Vergleichswert durch Aufbau einer einmaligen SSL-Verbindung mit einem Server auf der anderen Seite der Welt abruft ... würden Sie verdammt sicher eine massive Leistung sehen Differenz und der Algorithmus ist STILL O (n). Die Kosten von O (1) für jeden Algorithmus sind signifikant und NICHT äquivalent!
Darron
4
StringBuilder sb = new StringBuilder();

for (int i = 0; i < fName.Length; i++)
{
   if (char.IsLetterOrDigit(fName[i]))
    {
       sb.Append(fName[i]);
    }
}
Chamika Sandamal
quelle
4

Sie können den regulären Ausdruck wie folgt verwenden:

return Regex.Replace(strIn, @"[^\w\.@-]", "", RegexOptions.None, TimeSpan.FromSeconds(1.0));
Giovanny Farto M.
quelle
3

Es scheint mir gut zu sein. Die einzige Verbesserung, die ich machen würde, ist die Initialisierung StringBuildermit der Länge der Zeichenfolge.

StringBuilder sb = new StringBuilder(str.Length);
bruno conde
quelle
3

Ich stimme diesem Codebeispiel zu. Der einzige Unterschied ist, dass ich es in die Erweiterungsmethode vom Typ String mache. Damit Sie es in einer sehr einfachen Zeile oder einem Code verwenden können:

string test = "abc@#$123";
test.RemoveSpecialCharacters();

Vielen Dank an Guffa für Ihr Experiment.

public static class MethodExtensionHelper
    {
    public static string RemoveSpecialCharacters(this string str)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in str)
            {
                if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
                {
                    sb.Append(c);
                }
            }
            return sb.ToString();
        }
}
Tola Ch.
quelle
2

Ich würde einen String-Ersatz durch einen regulären Ausdruck verwenden, der nach "Sonderzeichen" sucht und alle gefundenen Zeichen durch einen leeren String ersetzt.

Stephen Wrighton
quelle
+1 sicherlich weniger Code und wohl besser lesbar, wenn man Regex einmal ignoriert.
Kenny
1
@ Kenny - ich stimme zu. Die ursprüngliche Frage besagt sogar, dass die Saiten kurz sind - 10-30 Zeichen. Aber anscheinend denken viele Leute immer noch, dass wir CPU-Zeit im Sekundentakt verkaufen ...
Tom Bushell
Reguler Expressin funktioniert so faul. Es sollte also nicht immer verwendet werden.
RockOnGom
2

Ich musste etwas Ähnliches für die Arbeit tun, aber in meinem Fall musste ich alles filtern, was kein Buchstabe, keine Zahl oder kein Leerzeichen ist (aber Sie können es leicht an Ihre Bedürfnisse anpassen). Die Filterung erfolgt clientseitig in JavaScript, aber aus Sicherheitsgründen mache ich die Filterung auch serverseitig. Da ich davon ausgehen kann, dass die meisten Zeichenfolgen sauber sind, möchte ich das Kopieren der Zeichenfolge vermeiden, es sei denn, ich muss es wirklich tun. Dies führte mich zur folgenden Implementierung, die sowohl für saubere als auch für schmutzige Zeichenfolgen eine bessere Leistung erbringen sollte.

public static string EnsureOnlyLetterDigitOrWhiteSpace(string input)
{
    StringBuilder cleanedInput = null;
    for (var i = 0; i < input.Length; ++i)
    {
        var currentChar = input[i];
        var charIsValid = char.IsLetterOrDigit(currentChar) || char.IsWhiteSpace(currentChar);

        if (charIsValid)
        {
            if(cleanedInput != null)
                cleanedInput.Append(currentChar);
        }
        else
        {
            if (cleanedInput != null) continue;
            cleanedInput = new StringBuilder();
            if (i > 0)
                cleanedInput.Append(input.Substring(0, i));
        }
    }

    return cleanedInput == null ? input : cleanedInput.ToString();
}
Daniel Blankensteiner
quelle
1

Für S & Gs, Linq-ified Weg:

var original = "(*^%foo)(@)&^@#><>?:\":';=-+_";
var valid = new char[] { 
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 
    'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 
    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', 
    '9', '0', '.', '_' };
var result = string.Join("",
    (from x in original.ToCharArray() 
     where valid.Contains(x) select x.ToString())
        .ToArray());

Ich denke jedoch nicht, dass dies der effizienteste Weg sein wird.


quelle
2
Es ist nicht, weil es eine lineare Suche ist.
Steven Sudit
1
public string RemoveSpecial(string evalstr)
{
StringBuilder finalstr = new StringBuilder();
            foreach(char c in evalstr){
            int charassci = Convert.ToInt16(c);
            if (!(charassci >= 33 && charassci <= 47))// special char ???
             finalstr.append(c);
            }
return finalstr.ToString();
}
Shiko
quelle
1

Verwenden:

s.erase(std::remove_if(s.begin(), s.end(), my_predicate), s.end());

bool my_predicate(char c)
{
 return !(isalpha(c) || c=='_' || c==' '); // depending on you definition of special characters
}

Und du bekommst eine saubere Schnur s.

erase()entfernt alle Sonderzeichen und ist mit der my_predicate()Funktion hochgradig anpassbar .

Bhavya Agarwal
quelle
1

HashSet ist O (1)
Nicht sicher, ob es schneller als der vorhandene Vergleich ist

private static HashSet<char> ValidChars = new HashSet<char>() { 'a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', '_' };
public static string RemoveSpecialCharacters(string str)
{
    StringBuilder sb = new StringBuilder(str.Length / 2);
    foreach (char c in str)
    {
        if (ValidChars.Contains(c)) sb.Append(c);
    }
    return sb.ToString();
}

Ich habe getestet und dies nicht schneller als die akzeptierte Antwort.
Ich werde es so lassen, als ob Sie einen konfigurierbaren Satz von Zeichen benötigen würden. Dies wäre eine gute Lösung.

Paparazzo
quelle
Warum denkst du, ist der Vergleich nicht O (1)?
Guffa
@ Guffa Ich bin nicht sicher, ob es nicht ist und ich habe meinen Kommentar entfernt. Und +1. Ich hätte mehr Tests durchführen sollen, bevor ich den Kommentar abgegeben habe.
Paparazzo
1

Ich frage mich, ob ein Regex-basierter Ersatz (möglicherweise kompiliert) schneller ist. Müsste das testen jemand festgestellt hat, dass dies ~ 5 mal langsamer ist.

Ansonsten sollten Sie den StringBuilder mit einer erwarteten Länge initialisieren, damit der Zwischenstring nicht kopiert werden muss, während er wächst.

Eine gute Zahl ist die Länge der ursprünglichen Zeichenfolge oder etwas etwas niedrigeres (abhängig von der Art der Funktionseingaben).

Schließlich können Sie eine Nachschlagetabelle (im Bereich 0..127) verwenden, um herauszufinden, ob ein Zeichen akzeptiert werden soll.

Christian Klauser
quelle
Ein regulärer Ausdruck wurde bereits getestet und ist ungefähr fünfmal langsamer. Bei einer Nachschlagetabelle im Bereich 0..127 müssen Sie den Zeichencode noch überprüfen, bevor Sie die Nachschlagetabelle verwenden, da Zeichen 16-Bit-Werte und keine 7-Bit-Werte sind.
Guffa
@ Guffa Err ... ja? ;)
Christian Klauser
1

Der folgende Code hat die folgende Ausgabe (Schlussfolgerung ist, dass wir auch einige Speicherressourcen sparen können, die dem Array eine kleinere Größe zuweisen):

lookup = new bool[123];

for (var c = '0'; c <= '9'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'A'; c <= 'Z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'a'; c <= 'z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

48: 0  
49: 1  
50: 2  
51: 3  
52: 4  
53: 5  
54: 6  
55: 7  
56: 8  
57: 9  
65: A  
66: B  
67: C  
68: D  
69: E  
70: F  
71: G  
72: H  
73: I  
74: J  
75: K  
76: L  
77: M  
78: N  
79: O  
80: P  
81: Q  
82: R  
83: S  
84: T  
85: U  
86: V  
87: W  
88: X  
89: Y  
90: Z  
97: a  
98: b  
99: c  
100: d  
101: e  
102: f  
103: g  
104: h  
105: i  
106: j  
107: k  
108: l  
109: m  
110: n  
111: o  
112: p  
113: q  
114: r  
115: s  
116: t  
117: u  
118: v  
119: w  
120: x  
121: y  
122: z  

Sie können auch die folgenden Codezeilen hinzufügen, um das russische Gebietsschema zu unterstützen (die Arraygröße beträgt 1104):

for (var c = 'А'; c <= 'Я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'а'; c <= 'я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}
Pavel Shkleinik
quelle
1

Ich bin nicht sicher, ob es der effizienteste Weg ist, aber es funktioniert für mich

 Public Function RemoverTildes(stIn As String) As String
    Dim stFormD As String = stIn.Normalize(NormalizationForm.FormD)
    Dim sb As New StringBuilder()

    For ich As Integer = 0 To stFormD.Length - 1
        Dim uc As UnicodeCategory = CharUnicodeInfo.GetUnicodeCategory(stFormD(ich))
        If uc <> UnicodeCategory.NonSpacingMark Then
            sb.Append(stFormD(ich))
        End If
    Next
    Return (sb.ToString().Normalize(NormalizationForm.FormC))
End Function
RonaldPaguay
quelle
Die Antwort funktioniert , aber die Frage war für C #. (PS: Ich weiß, dass dies praktisch vor fünf Jahren war, aber immer noch ..) Ich habe den Telerik VB to C # Converter verwendet (und umgekehrt) und der Code hat einwandfrei funktioniert - ich bin mir jedoch bei niemand anderem sicher. (Eine andere Sache, converter.telerik.com )
Momoro
1

Hier werden viele Lösungen vorgeschlagen, von denen einige effizienter als andere sind, aber möglicherweise nicht sehr gut lesbar. Hier ist eine, die möglicherweise nicht die effizienteste ist, aber für die meisten Situationen durchaus verwendbar ist. Sie ist recht präzise und lesbar und nutzt Linq:

string stringToclean = "This is a test.  Do not try this at home; you might get hurt. Don't believe it?";

var validPunctuation = new HashSet<char>(". -");

var cleanedVersion = new String(stringToclean.Where(x => (x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());

var cleanedLowercaseVersion = new String(stringToclean.ToLower().Where(x => (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());
Steve Faiwiszewski
quelle
-1
public static string RemoveSpecialCharacters(string str){
    return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
}
Jawaid
quelle
1
Ich fürchte, es replaceAllist keine C # String-Funktion, sondern entweder Java oder JavaScript
Csaba Toth
-1
public static string RemoveAllSpecialCharacters(this string text) {
  if (string.IsNullOrEmpty(text))
    return text;

  string result = Regex.Replace(text, "[:!@#$%^&*()}{|\":?><\\[\\]\\;'/.,~]", " ");
  return result;
}
Hasan_H
quelle
Die Antwort ist falsch. Wenn Sie Regex verwenden möchten, sollte dieser inklusive und nicht exklusiv sein, da Sie jetzt einige Zeichen vermissen. Eigentlich gibt es schon eine Antwort mit Regex. Und um voll zu sein - Regex ist langsamer als die Funktion zum direkten Vergleichen von Zeichen.
TPAKTOPA
-3

Wenn Sie sich Gedanken über die Geschwindigkeit machen, verwenden Sie Zeiger, um die vorhandene Zeichenfolge zu bearbeiten. Sie könnten die Zeichenfolge anheften und einen Zeiger darauf erhalten, dann eine for-Schleife über jedes Zeichen ausführen und jedes ungültige Zeichen mit einem Ersatzzeichen überschreiben. Es wäre äußerst effizient und würde keine Zuweisung eines neuen Zeichenfolgenspeichers erfordern. Sie müssten Ihr Modul auch mit der Option "unsicher" kompilieren und den Modifikator "unsicher" zu Ihrem Methodenheader hinzufügen, um Zeiger zu verwenden.

static void Main(string[] args)
{
    string str = "string!$%with^&*invalid!!characters";
    Console.WriteLine( str ); //print original string
    FixMyString( str, ' ' );
    Console.WriteLine( str ); //print string again to verify that it has been modified
    Console.ReadLine(); //pause to leave command prompt open
}


public static unsafe void FixMyString( string str, char replacement_char )
{
    fixed (char* p_str = str)
    {
        char* c = p_str; //temp pointer, since p_str is read-only
        for (int i = 0; i < str.Length; i++, c++) //loop through each character in string, advancing the character pointer as well
            if (!IsValidChar(*c)) //check whether the current character is invalid
                (*c) = replacement_char; //overwrite character in existing string with replacement character
    }
}

public static bool IsValidChar( char c )
{
    return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '.' || c == '_');
    //return char.IsLetterOrDigit( c ) || c == '.' || c == '_'; //this may work as well
}
Triynko
quelle
14
Noooooooooo! Das Ändern einer Zeichenfolge in .NET ist BAAAAAAAAAAAAD! Alles im Framework basiert auf der Regel, dass Strings unveränderlich sind, und wenn Sie brechen, können Sie sehr überraschende Nebenwirkungen bekommen ...
Guffa