Maneira mais eficiente de remover caracteres especiais da string

266

Quero remover todos os caracteres especiais de uma string. Os caracteres permitidos são AZ (maiúsculas ou minúsculas), números (0-9), sublinhado (_) ou o sinal de ponto (.).

Eu tenho o seguinte, funciona, mas desconfio (eu sei!) Que não é muito eficiente:

    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 é a maneira mais eficiente de fazer isso? Como seria uma expressão regular e como ela se compara à manipulação normal de strings?

As strings que serão limpas serão bastante curtas, geralmente entre 10 e 30 caracteres.

Obi wan Kenobi
fonte
5
Não colocarei isso em uma resposta, pois não será mais eficiente, mas há vários métodos estáticos de char, como char.IsLetterOrDigit (), que você pode usar na instrução if para torná-lo mais legível, pelo menos.
1013 Martin Martin
5
Não tenho certeza se a verificação de A a z é segura, pois traz 6 caracteres que não são alfabéticos, apenas um deles é desejado (abaixo da barra).
Steven Sudit
4
Concentre-se em tornar seu código mais legível. a menos que você esteja fazendo isso repetidamente 500 vezes por segundo, a eficiência não é grande coisa. Use uma expressão regular e será muito mais fácil de read.l
Byron Whitlock
4
Byron, você provavelmente está certo sobre a necessidade de enfatizar a legibilidade. No entanto, sou cético quanto à possibilidade de o regexp ser legível. :-)
Steven Sudit 13/07/2009
2
Expressões regulares sendo legíveis ou não são como o alemão sendo legível ou não; isso depende se você sabe ou não (embora em ambos os casos, você vai de vez em quando se deparar com as regras gramaticais que não fazem sentido;)
Blixt

Respostas:

325

Por que você acha que seu método não é eficiente? Na verdade, é uma das maneiras mais eficientes de fazer isso.

Obviamente, você deve ler o caractere em uma variável local ou usar um enumerador para reduzir o número de acessos à matriz:

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

Uma coisa que torna um método como esse eficiente é que ele se adapta bem. O tempo de execução será relativo ao comprimento da string. Não há surpresas desagradáveis ​​se você a usar em uma corda grande.

Edit:
Fiz um teste rápido de desempenho, executando cada função um milhão de vezes com uma sequência de 24 caracteres. Estes são os resultados:

Função original: 54,5 ms.
Minha alteração sugerida: 47,1 ms.
Mina com configuração da capacidade do StringBuilder: 43,3 ms.
Expressão regular: 294,4 ms.

Edit 2: eu adicionei a distinção entre AZ e az no código acima. (Eu refiz o teste de desempenho e não há diferença perceptível.)

Edit 3:
Eu testei a solução lookup + char [], e ela é executada em cerca de 13 ms.

O preço a pagar é, obviamente, a inicialização da enorme tabela de pesquisa e a memória. Bem, não há muitos dados, mas é uma função tão trivial ...

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
fonte
4
Concordo. A única outra alteração que eu faria é adicionar o argumento de capacidade inicial ao construtor StringBuilder, "= new StringBuilder (str.Length)".
David
2
Minha resposta, usando um char[]buffer em vez de StringBuilder, tem uma ligeira vantagem neste, de acordo com meus testes. (Embora menos legível do meu, então a pequena vantagem de desempenho provavelmente não vale a pena.)
LukeH
1
@ Steven: Isso pode muito bem ser o caso, mas os benchmarks falam por si! Nos meus testes, o uso de um char[]buffer tem um desempenho (ligeiramente) melhor que StringBuilder, mesmo ao escalar para seqüências de caracteres com dezenas de milhares de caracteres.
12119 LukeH
10
@ downvoter: Por que o voto negativo? Se você não explicar o que acha errado, não poderá melhorar a resposta.
Guffa 6/08/11
2
@ SILENT: Não, não, mas você deve fazer isso apenas uma vez. Se você alocar uma matriz tão grande toda vez que chamar o método (e se chamar frequentemente o método), o método se tornará o mais lento de longe e causará muito trabalho para o coletor de lixo.
Guffa
195

Bem, a menos que você realmente precise reduzir o desempenho de sua função, basta seguir o que é mais fácil de manter e entender. Uma expressão regular ficaria assim:

Para um desempenho adicional, você pode pré-compilar ou apenas pedir para compilar na primeira chamada (as chamadas subseqüentes serão mais rápidas).

public static string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}
Blixt
fonte
1
Eu acho que essa provavelmente é uma consulta complexa o suficiente para ser mais rápida que a abordagem do OP, especialmente se pré-compilada. No entanto, não tenho evidências para comprovar isso. Deve ser testado. A menos que seja drasticamente mais lento, eu escolheria essa abordagem independentemente, já que é muito mais fácil ler e manter. 1
rmeador 13/07/2009
6
É um regex muito simples (sem retorno ou qualquer coisa complexa), portanto deve ser bem rápido.
9
@rmeador: sem ser compilado, é cerca de 5x mais lento, compilado é 3x mais lento que o método dele. Ainda 10x mais simples:
:: 7116
6
Expressões regulares não são martelos mágicos e nunca são mais rápidas que o código otimizado manualmente.
Christian Klauser
2
Para quem se lembra da famosa citação de Knuth sobre otimização, é por aqui que começa. Então, se você achar que precisa do milésimo de milésimo de segundo extra de desempenho, siga uma das outras técnicas.
John John
15

Sugiro criar uma tabela de pesquisa simples, que você pode inicializar no construtor estático para definir qualquer combinação de caracteres como válida. Isso permite que você faça uma verificação rápida e única.

editar

Além disso, para velocidade, você desejará inicializar a capacidade do seu StringBuilder para o comprimento da sua string de entrada. Isso evitará realocações. Esses dois métodos juntos oferecem velocidade e flexibilidade.

outra edição

Eu acho que o compilador pode otimizá-lo, mas por uma questão de estilo e eficiência, eu recomendo foreach em vez de for.

Steven Sudit
fonte
Para matrizes fore foreachproduza código semelhante. Eu não sei sobre cordas embora. Duvido que o JIT conheça a natureza de String do tipo array.
Christian Klauser
1
Aposto que o JIT sabe mais sobre a natureza da cadeia de caracteres do que sua [piada removida]. Anders et al fez um monte de trabalho otimizando tudo sobre cordas em .net
Eu fiz isso usando o HashSet <char> e é cerca de duas vezes mais lento que o método dele. O uso de bool [] é pouco mais rápido (0,0469ms / iter vs. 0,0559ms / iter) do que a versão que ele possui no OP ... com o problema de ser menos legível.
User7116 13/07/2009
1
Não pude ver nenhuma diferença de desempenho entre o uso de uma matriz bool e uma matriz int. Eu usaria uma matriz bool, pois reduz a tabela de pesquisa de 256 kb para 64 kb, mas ainda há muitos dados para uma função tão trivial ... E é apenas 30% mais rápido.
Guffa
1
@ Guffa 2) Dado que mantemos apenas alfanuméricos e alguns caracteres latinos básicos, precisamos apenas de uma tabela para o byte baixo, para que o tamanho não seja realmente um problema. Se queremos ser de uso geral, a técnica Unicode padrão é de dupla indireção. Em outras palavras, uma tabela de 256 referências de tabela, muitas das quais apontam para a mesma tabela vazia.
911 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
fonte
1
+1, testado e é cerca de 40% mais rápido que o StringBuilder. 0.0294ms / string v. 0.0399ms / string
user7116
Só para ter certeza, você quer dizer StringBuilder com ou sem pré-alocação?
911 Steven Sudit #
Com a pré-alocação, ele ainda é 40% mais lento que a alocação de char [] e a nova string.
User7116
2
Eu gosto disso. Eu tweaked este métodoforeach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Chris Marisic
11

Uma expressão regular será semelhante a:

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

Mas se o desempenho for muito importante, recomendo que você faça alguns benchmarks antes de selecionar o "caminho da expressão regular" ...

CMS
fonte
11

Se você estiver usando uma lista dinâmica de caracteres, o LINQ poderá oferecer uma solução muito mais rápida e elegante:

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

Comparei essa abordagem com duas das abordagens "rápidas" anteriores (compilação de versão):

  • Char array solution por LukeH - 427 ms
  • Solução StringBuilder - 429 ms
  • LINQ (esta resposta) - 98 ms

Observe que o algoritmo é ligeiramente modificado - os caracteres são passados ​​como uma matriz em vez de codificados, o que pode impactar um pouco as coisas (ou seja, as outras soluções teriam um loop interno para verificar a matriz de caracteres).

Se eu mudar para uma solução codificada usando uma cláusula LINQ where, os resultados serão:

  • Solução de matriz de caracteres - 7ms
  • Solução StringBuilder - 22ms
  • LINQ - 60 ms

Pode valer a pena olhar para o LINQ ou uma abordagem modificada se você estiver planejando escrever uma solução mais genérica, em vez de codificar a lista de caracteres. Definitivamente, o LINQ fornece um código conciso e altamente legível - ainda mais que o Regex.

ShadowChaser
fonte
3
Essa abordagem parece boa, mas não funciona - Except () é uma operação definida, portanto você terá apenas a primeira aparência de cada caractere exclusivo da string.
McKenzieG1
5

Não estou convencido de que seu algoritmo seja tudo menos eficiente. É O (n) e só olha para cada personagem uma vez. Você não vai ficar melhor do que isso, a menos que saiba magicamente os valores antes de verificá-los.

No entanto, eu inicializaria a capacidade do seu StringBuilderpara o tamanho inicial da string. Suponho que seu problema de desempenho percebido vem da realocação de memória.

Nota lateral: Verificação A- znão é seguro. Você está incluindo [, \, ], ^, _, e `...

Nota lateral 2: Para obter um pouco mais de eficiência, faça as comparações em ordem para minimizar o número de comparações. (Na pior das hipóteses, você está falando 8 comparações, não pense muito.) Isso muda com a entrada esperada, mas um exemplo pode ser:

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

Nota lateral 3: Se, por qualquer motivo, você REALMENTE precisar que isso seja rápido, uma declaração de chave poderá ser mais rápida. O compilador deve criar uma tabela de salto para você, resultando em apenas uma única comparação:

switch (str[i])
{
    case '0':
    case '1':
    .
    .
    .
    case '.':
        sb.Append(str[i]);
        break;
}
lc.
fonte
1
Concordo que você não pode vencer O (n) neste. No entanto, existe um custo por comparação que pode ser reduzido. Uma pesquisa de tabela tem um custo fixo baixo, enquanto uma série de comparações aumentará em custos à medida que você adiciona mais exceções.
Steven Sudit
Sobre a nota lateral 3, você realmente acha que a tabela de salto seria mais rápida que a pesquisa da tabela?
911 Steven Sudit
Fiz o teste rápido de desempenho na solução do switch e ele executa o mesmo da comparação.
Guffa
@ Steven Sudit - eu arriscaria que eles são sobre o mesmo. Gostaria de executar um teste?
lc.
7
Às vezes, a notação O (n) me irrita. As pessoas farão suposições estúpidas com base no fato de o algoritmo já ser O (n). Se mudarmos essa rotina para substituir as chamadas str [i] por uma função que recuperou o valor de comparação, construindo uma conexão SSL única com um servidor no lado oposto do mundo ... você com certeza verá um desempenho enorme diferença e o algoritmo é AINDA O (n). O custo de O (1) para cada algoritmo é significativo e NÃO equivalente!
1111 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
fonte
4

Você pode usar expressões regulares da seguinte maneira:

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

Parece bom para mim. A única melhoria que eu faria seria inicializar o StringBuildercom o comprimento da string.

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

Eu concordo com este exemplo de código. O único diferente que eu faço no método de extensão do tipo string. Para que você possa usá-lo em uma linha ou código muito simples:

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

Agradeço a Guffa por sua experiência.

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.
fonte
2

Eu usaria uma String Substituir por uma Expressão Regular procurando "caracteres especiais", substituindo todos os caracteres encontrados por uma string vazia.

Stephen Wrighton
fonte
+1 com certeza menos código e possivelmente mais legível ignorando o Regex de gravação única.
Kenny
1
@kenny - eu concordo. A pergunta original ainda afirma que as strings são curtas - 10 a 30 caracteres. Mas, aparentemente, muitas pessoas ainda pensam que estamos vendendo tempo de CPU a cada segundo ...
Tom Bushell
O regulin expressin funciona tão preguiçoso. Portanto, não deve ser usado sempre.
RockOnGom 10/07/2013
2

Eu tive que fazer algo semelhante para o trabalho, mas no meu caso eu tive que filtrar tudo o que não é uma letra, número ou espaço em branco (mas você pode modificá-lo facilmente para suas necessidades). A filtragem é feita no lado do cliente em JavaScript, mas por motivos de segurança, também estou fazendo a filtragem no lado do servidor. Como posso esperar que a maioria das seqüências de caracteres esteja limpa, gostaria de evitar copiar a sequência, a menos que realmente precise. Isso permitiu a implementação abaixo, que deve ter um desempenho melhor para as strings limpas e sujas.

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
fonte
1

Para S&G, 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());

Eu não acho que essa seja a maneira mais eficiente, no entanto.


fonte
2
Não é, porque é uma pesquisa linear.
911 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
fonte
1

Usar:

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 você terá uma string limpa s.

erase()tira todos os caracteres especiais e é altamente personalizável com a my_predicate()função

Bhavya Agarwal
fonte
1

HashSet é O (1)
Não tenho certeza se é mais rápido que a comparação existente

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

Eu testei e isso não é mais rápido que a resposta aceita.
Vou deixar como se você precisasse de um conjunto configurável de caracteres, seria uma boa solução.

paparazzo
fonte
Por que você acha que a comparação não é O (1)?
Guffa
@ Guffa Não tenho certeza se não é e removi meu comentário. E +1. Eu deveria ter feito mais testes antes de fazer o comentário.
paparazzo
1

Gostaria de saber se uma substituição baseada em Regex (possivelmente compilada) é mais rápida. Teria que testar isso alguém descobriu que isso era ~ 5 vezes mais lento.

Fora isso, você deve inicializar o StringBuilder com um comprimento esperado, para que a cadeia intermediária não precise ser copiada enquanto cresce.

Um bom número é o comprimento da string original, ou algo um pouco menor (dependendo da natureza das entradas das funções).

Por fim, você pode usar uma tabela de pesquisa (no intervalo de 0 a 127) para descobrir se um caractere deve ser aceito.

Christian Klauser
fonte
Uma expressão regular já foi testada e é cerca de cinco vezes mais lenta. Com uma tabela de pesquisa no intervalo de 0..127, você ainda precisa verificar o código de caracteres antes de usar a tabela de pesquisa, pois os caracteres são valores de 16 bits e não de 7 bits.
Guffa
@Guffa Err ... sim? ;)
Christian Klauser
1

O código a seguir tem a seguinte saída (conclusão é que também podemos economizar alguns recursos de memória alocando tamanho menor da matriz):

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  

Você também pode adicionar as seguintes linhas de código para dar suporte à localidade russa (o tamanho da matriz será 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
fonte
1

Não sei se é a maneira mais eficiente, mas funciona para mim

 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
fonte
A resposta faz o trabalho, mas a questão era para C #. (PS: Eu sei que isso foi há quase cinco anos, mas ainda assim ..) Eu usei o Telerik VB to C # Converter, (e vice-versa) e o código funcionou muito bem - embora não tenha certeza de mais ninguém. (Outra coisa, converter.telerik.com )
Momoro 18/04
1

Existem muitas soluções propostas aqui, algumas mais eficientes que outras, mas talvez não sejam muito legíveis. Aqui está um que pode não ser o mais eficiente, mas certamente utilizável na maioria das situações, e é bastante conciso e legível, aproveitando o 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
fonte
-1
public static string RemoveSpecialCharacters(string str){
    return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
}
Jawaid
fonte
1
Eu tenho medo replaceAllnão é função C # Cordas mas Java ou 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
fonte
A resposta está errada. Se você usará o regex, ele deve ser inclusivo, e não exclusivo, porque você sente falta de alguns caracteres agora. Na verdade, já existe resposta com regex. E para estar cheio - regex é MAIS LENTO, então compare diretamente a função chars.
TPAKTOPA 13/06
-3

Se você está preocupado com a velocidade, use ponteiros para editar a string existente. Você pode fixar a sequência e obter um ponteiro para ela e, em seguida, executar um loop for sobre cada caractere, substituindo cada caractere inválido por um caractere de substituição. Seria extremamente eficiente e não exigiria a alocação de nenhuma nova memória de string. Você também precisaria compilar seu módulo com a opção não segura e adicionar o modificador "não seguro" ao cabeçalho do método para usar ponteiros.

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
fonte
14
Noooooooooo! Alterar uma string no .NET é BAAAAAAAAAAAAD! Tudo na estrutura se baseia na regra de que as strings são imutáveis ​​e, se você quebrar, poderá obter efeitos colaterais muito surpreendentes ...
Guffa