Il modo più efficace per rimuovere caratteri speciali dalla stringa


266

Voglio rimuovere tutti i caratteri speciali da una stringa. I caratteri consentiti sono AZ (maiuscolo o minuscolo), numeri (0-9), trattino basso (_) o il segno punto (.).

Ho il seguente, funziona ma sospetto (lo so!) Non è molto efficiente:

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

Qual è il modo più efficiente per farlo? Come sarebbe un'espressione regolare e come si confronta con la normale manipolazione delle stringhe?

Le stringhe che verranno pulite saranno piuttosto corte, in genere lunghe tra 10 e 30 caratteri.


5
Non lo inserirò in una risposta poiché non sarà più efficiente, ma ci sono un certo numero di metodi statici di char come char.IsLetterOrDigit () che potresti usare nell'istruzione if per renderlo almeno più leggibile.
Martin Harris,

5
Non sono sicuro che controllare dalla A alla Z sia sicuro, in quanto porta a 6 caratteri che non sono alfabetici, solo uno dei quali è desiderato (underbar).
Steven Sudit,

4
Concentrati sul rendere il tuo codice più leggibile. a meno che non lo facciate in un ciclo come 500 volte al secondo, l'efficienza non è un grosso problema. Usa una regexp e sarà molto più facile da leggere.
Byron Whitlock,

4
Byron, probabilmente hai ragione sulla necessità di enfatizzare la leggibilità. Tuttavia, sono scettico sul fatto che regexp sia leggibile. :-)
Steven Sudit,

2
Le espressioni regolari essere leggibili o no è un po 'come se il tedesco fosse leggibile o no; dipende dal fatto che tu lo sappia o meno (anche se in entrambi i casi ti imbatterai di tanto in tanto in regole grammaticali che non hanno alcun senso;)
Blixt

Risposte:


325

Perché pensi che il tuo metodo non sia efficiente? In realtà è uno dei modi più efficienti per farlo.

Ovviamente dovresti leggere il carattere in una variabile locale o usare un enumeratore per ridurre il numero di accessi all'array:

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

Una cosa che rende efficiente un metodo come questo è che si ridimensiona bene. Il tempo di esecuzione sarà relativo alla lunghezza della stringa. Non ci sono brutte sorprese se lo utilizzassi su una stringa di grandi dimensioni.

Modificare:
ho eseguito un rapido test delle prestazioni, eseguendo ogni funzione un milione di volte con una stringa di 24 caratteri. Questi sono i risultati:

Funzione originale: 54,5 ms.
La mia modifica suggerita: 47.1 ms.
Miniera con impostazione della capacità di StringBuilder: 43.3 ms.
Espressione regolare: 294,4 ms.

Modifica 2: ho aggiunto la distinzione tra AZ e az nel codice sopra. (Eseguo nuovamente il test delle prestazioni e non vi è alcuna differenza evidente.)

Modifica 3:
ho testato la soluzione lookup + char [] e viene eseguita in circa 13 ms.

Il prezzo da pagare è, ovviamente, l'inizializzazione dell'enorme tabella di ricerca e la sua memorizzazione. Bene, non sono molti dati, ma è molto per una funzione così banale ...

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

4
Sono d'accordo. L'unica altra modifica che vorrei fare è aggiungere l'argomento capacità iniziale al costruttore StringBuilder, "= new StringBuilder (str.Length)".
David,

2
La mia risposta, usando un char[]buffer anziché StringBuilder, ha un leggero vantaggio su questo secondo i miei test. (Il mio è meno leggibile, quindi il piccolo vantaggio in termini di prestazioni probabilmente non ne vale la pena.)
LukeH,

1
@Steven: Potrebbe anche essere il caso, ma i parametri di riferimento parlano da soli! Nei miei test, l'utilizzo di un char[]buffer ha prestazioni (leggermente) migliori rispetto a StringBuilder, anche quando si esegue il ridimensionamento a stringhe lunghe decine di migliaia di caratteri.
Luca,

10
@downvoter: Perché il downvote? Se non spieghi cosa pensi sia sbagliato, non è possibile migliorare la risposta.
Guffa,

2
@SILENT: No, non è così, ma dovresti farlo solo una volta. Se si assegna una matrice così grande ogni volta che si chiama il metodo (e se si chiama frequentemente il metodo), il metodo diventa di gran lunga il più lento e provoca molto lavoro per il Garbage Collector.
Guffa,

195

Bene, a meno che tu non abbia davvero bisogno di spremere le prestazioni dalla tua funzione, basta andare con ciò che è più facile da mantenere e capire. Un'espressione regolare sarebbe simile a questa:

Per prestazioni aggiuntive, è possibile pre-compilarlo o semplicemente dirlo di compilare alla prima chiamata (le chiamate successive saranno più veloci).

public static string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}

1
Immagino che questa sia probabilmente una query abbastanza complessa da essere più veloce dell'approccio del PO, specialmente se pre-compilato. Non ho prove a sostegno di ciò, tuttavia. Dovrebbe essere testato. A meno che non sia drasticamente più lento, sceglierei questo approccio a prescindere, poiché è molto più facile da leggere e mantenere. +1
rmeador,

6
È una regex molto semplice (senza backtracking o roba complessa) quindi dovrebbe essere abbastanza veloce.

9
@rmeador: senza essere compilato è circa 5 volte più lento, compilato è 3 volte più lento del suo metodo. Ancora 10 volte più semplice però :-D
user7116

6
Le espressioni regolari non sono martelli magici e mai più veloci del codice ottimizzato a mano.
Christian Klauser,

2
Per coloro che ricordano la famosa citazione di Knuth sull'ottimizzazione, questo è il punto di partenza. Quindi, se scopri che hai bisogno della prestazione extra di un millesimo di secondo, scegli una delle altre tecniche.
Giovanni,

15

Suggerisco di creare una semplice tabella di ricerca, che è possibile inizializzare nel costruttore statico per impostare qualsiasi combinazione di caratteri su valida. Ciò consente di eseguire un controllo rapido e singolo.

modificare

Inoltre, per la velocità, ti consigliamo di inizializzare la capacità del StringBuilder alla lunghezza della stringa di input. Ciò eviterà riallocazioni. Questi due metodi insieme ti daranno velocità e flessibilità.

un'altra modifica

Penso che il compilatore potrebbe ottimizzarlo, ma per quanto riguarda lo stile e l'efficienza, raccomando foreach anziché for.


Per array fore foreachproduce codice simile. Non conosco le stringhe però. Dubito che JIT sia a conoscenza della natura simile a matrice di String.
Christian Klauser,

1
Scommetto che il JIT sa di più sulla natura simile a matrice della stringa rispetto al tuo [scherzo rimosso]. Anders etal ha lavorato molto per ottimizzare tutto ciò che riguarda le stringhe in .net

L'ho fatto usando HashSet <char> ed è circa 2 volte più lento del suo metodo. L'uso di bool [] è appena più veloce (0.0469ms / iter v. 0.0559ms / iter) rispetto alla versione che ha in OP ... con il problema di essere meno leggibile.
user7116,

1
Non ho riscontrato alcuna differenza di prestazioni tra l'utilizzo di un array bool e un array int. Vorrei usare un array bool, poiché porta la tabella di ricerca da 256 KB a 64 KB, ma sono ancora molti dati per una funzione così banale ... Ed è solo circa il 30% più veloce.
Guffa,

1
@Guffa 2) Dato che manteniamo solo caratteri alfanumerici e alcuni caratteri latini di base, abbiamo solo bisogno di una tabella per il byte basso, quindi la dimensione non è davvero un problema. Se volessimo essere generici, la tecnica Unicode standard è la doppia indiretta. In altre parole, una tabella di 256 riferimenti di tabella, molti dei quali puntano alla stessa tabella vuota.
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);
}

1
+1, testato ed è circa il 40% più veloce di StringBuilder. 0.0294ms / stringa v. 0.0399ms / stringa
user7116

Per sicurezza, intendi StringBuilder con o senza pre-allocazione?
Steven Sudit,

Con la pre-allocazione, è ancora più lenta del 40% rispetto all'allocazione char [] e alla nuova stringa.
user7116,

2
Mi piace questo. Ho modificato questo metodoforeach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Chris Marisic il

11

Un'espressione regolare sarà simile a:

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

Ma se le prestazioni sono molto importanti, ti consiglio di fare alcuni benchmark prima di selezionare il "percorso regex" ...


11

Se stai usando un elenco dinamico di caratteri, LINQ potrebbe offrire una soluzione molto più veloce e aggraziata:

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

Ho confrontato questo approccio con due dei precedenti approcci "veloci" (compilazione delle versioni):

  • Soluzione Char array di LukeH - 427 ms
  • Soluzione StringBuilder - 429 ms
  • LINQ (questa risposta) - 98 ms

Si noti che l'algoritmo è leggermente modificato: i caratteri vengono passati come array piuttosto che hardcoded, il che potrebbe avere un leggero impatto sulle cose (cioè / le altre soluzioni avrebbero un ciclo interno per controllare l'array di caratteri).

Se passo a una soluzione hard-coded usando una clausola LINQ where, i risultati sono:

  • Soluzione Char array - 7ms
  • Soluzione StringBuilder - 22ms
  • LINQ - 60 ms

Potrebbe valere la pena guardare LINQ o un approccio modificato se hai intenzione di scrivere una soluzione più generica, piuttosto che codificare a fondo l'elenco di caratteri. LINQ ti offre sicuramente un codice conciso e altamente leggibile, anche più di Regex.


3
Questo approccio sembra carino, ma non funziona - Except () è un'operazione impostata, quindi finirai con solo la prima apparizione di ogni carattere univoco nella stringa.
McKenzieG1

5

Non sono convinto che il tuo algoritmo sia tutt'altro che efficiente. È O (n) e guarda ogni personaggio una sola volta. Non otterrai niente di meglio se non conosci magicamente i valori prima di controllarli.

Vorrei tuttavia inizializzare la capacità del tuo StringBuilder alla dimensione iniziale della stringa. Immagino che il tuo problema di prestazioni percepito provenga dalla riallocazione della memoria.

Nota a margine: controllo A- znon è sicuro. Stai includendo[ , \, ], ^,_ , e `...

Nota a margine 2: per quel tocco in più di efficienza, metti i confronti in un ordine per minimizzare il numero di confronti. (Nel peggiore dei casi, stai parlando di 8 confronti, quindi non pensare troppo.) Questo cambia con il tuo input previsto, ma un esempio potrebbe essere:

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

Nota a margine 3: se per qualsiasi motivo hai davvero bisogno che questo sia veloce, un'istruzione switch potrebbe essere più veloce. Il compilatore dovrebbe creare una tabella di salto per te, risultando in un solo confronto:

switch (str[i])
{
    case '0':
    case '1':
    .
    .
    .
    case '.':
        sb.Append(str[i]);
        break;
}

1
Sono d'accordo che non puoi battere O (n) su questo. Tuttavia, esiste un costo per confronto che può essere ridotto. Una ricerca di tabella ha un costo fisso basso, mentre una serie di confronti aumenterà di costo quando si aggiungono ulteriori eccezioni.
Steven Sudit,

A proposito della nota a margine 3, pensi davvero che la tabella di salto sia più veloce della ricerca della tabella?
Steven Sudit,

Ho eseguito il test rapido delle prestazioni sulla soluzione switch ed esegue lo stesso del confronto.
Guffa,

@Steven Sudit - Mi sarei azzardato che in realtà sono più o meno gli stessi. Vuoi eseguire un test?
lc.

7
La notazione O (n) a volte mi fa incazzare. Le persone faranno ipotesi stupide in base al fatto che l'algoritmo è già O (n). Se cambiassimo questa routine per sostituire le chiamate str [i] con una funzione che ha recuperato il valore di confronto costruendo una connessione SSL singola con un server dalla parte opposta del mondo ... sei sicuro che vedresti prestazioni enormi differenza e l'algoritmo è ANCORA O (n). Il costo di O (1) per ciascun algoritmo è significativo e NON equivalente!
darron,

4
StringBuilder sb = new StringBuilder();

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

4

È possibile utilizzare l'espressione regolare come segue:

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

3

Mi sembra buono. L'unico miglioramento che vorrei fare è inizializzare il StringBuildercon la lunghezza della stringa.

StringBuilder sb = new StringBuilder(str.Length);

3

Sono d'accordo con questo esempio di codice. L'unico diverso lo trasformo in Metodo di estensione di tipo stringa. In modo che tu possa usarlo in una riga o un codice molto semplice:

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

Grazie a Guffa per il tuo esperimento.

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

2

Vorrei utilizzare una stringa Sostituisci con un'espressione regolare cercando "caratteri speciali", sostituendo tutti i caratteri trovati con una stringa vuota.


+1 sicuramente meno codice e probabilmente più leggibile ignorando Regex write-once.
Kenny,

1
@kenny - Sono d'accordo. La domanda originale afferma anche che le stringhe sono brevi - 10-30 caratteri. Ma a quanto pare molte persone pensano ancora che stiamo vendendo il tempo della CPU dal secondo ...
Tom Bushell,

L'espressione di Reguler funziona in modo così pigro, quindi non dovrebbe essere usata sempre.
RockOnGom,

2

Ho dovuto fare qualcosa di simile per lavoro, ma nel mio caso ho dovuto filtrare tutto ciò che non è una lettera, un numero o uno spazio bianco (ma potresti facilmente modificarlo in base alle tue esigenze). Il filtro viene eseguito sul lato client in JavaScript, ma per motivi di sicurezza sto facendo anche il filtro sul lato server. Dato che posso aspettarmi che la maggior parte delle stringhe sia pulita, vorrei evitare di copiare la stringa a meno che non sia realmente necessario. Questo mi ha permesso di implementare di seguito, che dovrebbe funzionare meglio per stringhe pulite e sporche.

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

1

Per S&G, modo Linq-ified:

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

Non penso che questo sarà il modo più efficiente, tuttavia.


2
Non lo è, perché è una ricerca lineare.
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();
}

1

Uso:

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
}

E otterrai una stringa pulita s.

erase()lo spoglia di tutti i caratteri speciali ed è altamente personalizzabile con la my_predicate()funzione.


1

HashSet è O (1)
Non sono sicuro che sia più veloce del confronto esistente

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

Ho provato e questo non è più veloce della risposta accettata.
Lo lascerò come se avessi bisogno di un set di caratteri configurabile, questa sarebbe una buona soluzione.


Perché pensi che il confronto non sia O (1)?
Guffa,

@Guffa Non sono sicuro che non lo sia e ho rimosso il mio commento. E +1. Avrei dovuto fare più test prima di fare il commento.
paparazzo,

1

Mi chiedo se una sostituzione basata su Regex (possibilmente compilata) sia più veloce. Dovrebbe testare che Qualcuno ha scoperto che questo è circa 5 volte più lento.

A parte questo, dovresti inizializzare StringBuilder con una lunghezza prevista, in modo che la stringa intermedia non debba essere copiata mentre cresce.

Un buon numero è la lunghezza della stringa originale o qualcosa di leggermente inferiore (a seconda della natura degli input delle funzioni).

Infine, puoi utilizzare una tabella di ricerca (nell'intervallo 0..127) per scoprire se un personaggio deve essere accettato.


Un'espressione regolare è già stata testata ed è circa cinque volte più lenta. Con una tabella di ricerca nell'intervallo 0..127 è comunque necessario verificare il codice del carattere prima di utilizzare la tabella di ricerca, poiché i caratteri sono valori a 16 bit, non a 7 bit.
Guffa,

@Guffa Err ... sì? ;)
Christian Klauser,

1

Il seguente codice ha il seguente output (la conclusione è che possiamo anche salvare alcune risorse di memoria allocando array di dimensioni inferiori):

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  

È inoltre possibile aggiungere le seguenti righe di codice per supportare le impostazioni locali russe (la dimensione dell'array sarà 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);
}

1

Non sono sicuro che sia il modo più efficiente, ma funziona per me

 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

La risposta fa il lavoro, ma la domanda era per C #. (PS: so che è stato praticamente cinque anni fa, ma comunque ..) Ho usato il convertitore Telerik VB in C #, (e viceversa) e il codice ha funzionato bene, ma non sono sicuro di nessun altro. (Un'altra cosa, converter.telerik.com )
Momoro,

1

Ci sono molte soluzioni proposte qui, alcune più efficienti di altre, ma forse non molto leggibili. Eccone uno che potrebbe non essere il più efficiente, ma sicuramente utilizzabile per la maggior parte delle situazioni, ed è abbastanza conciso e leggibile, sfruttando 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());

-1
public static string RemoveSpecialCharacters(string str){
    return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
}

1
Temo che la replaceAllfunzione C # String non sia Java o JavaScript
Csaba Toth,

-1
public static string RemoveAllSpecialCharacters(this string text) {
  if (string.IsNullOrEmpty(text))
    return text;

  string result = Regex.Replace(text, "[:!@#$%^&*()}{|\":?><\\[\\]\\;'/.,~]", " ");
  return result;
}

La risposta è sbagliata Se stai per usare regex, dovrebbe essere inclusivo, non esclusivo, perché manchi alcuni personaggi adesso. In realtà, c'è già una risposta con regex. E per essere completo, regex è SLOWER, quindi la funzione di confronto diretto dei caratteri.
TPAKTOPA,

-3

Se sei preoccupato per la velocità, usa i puntatori per modificare la stringa esistente. È possibile bloccare la stringa e ottenere un puntatore ad essa, quindi eseguire un ciclo for su ciascun carattere, sovrascrivendo ogni carattere non valido con un carattere sostitutivo. Sarebbe estremamente efficiente e non richiederebbe l'allocazione di alcuna nuova memoria stringa. Dovresti anche compilare il modulo con l'opzione non sicura e aggiungere il modificatore "non sicuro" all'intestazione del metodo per utilizzare i puntatori.

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
}

14
Noooooooooo! La modifica di una stringa in .NET è BAAAAAAAAAAAAD! Tutto nel framework si basa sulla regola secondo cui le stringhe sono immutabili e se si rompe si possono ottenere effetti collaterali molto sorprendenti ...
Guffa,
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.