Le moyen le plus efficace pour supprimer des caractères spéciaux de la chaîne

266

Je souhaite supprimer tous les caractères spéciaux d'une chaîne. Les caractères autorisés sont AZ (majuscule ou minuscule), les chiffres (0-9), le trait de soulignement (_) ou le signe point (.).

J'ai le suivant, ça marche mais je soupçonne (je sais!) Ce n'est pas très efficace:

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

Quelle est la manière la plus efficace de procéder? À quoi ressemblerait une expression régulière et comment se compare-t-elle à une manipulation de chaîne normale?

Les chaînes qui seront nettoyées seront plutôt courtes, généralement entre 10 et 30 caractères.

Obi Wan Kenobi
la source
5
Je ne mettrai pas cela dans une réponse car ce ne sera pas plus efficace, mais il existe un certain nombre de méthodes de caractères statiques comme char.IsLetterOrDigit () que vous pouvez utiliser dans votre instruction if pour le rendre plus lisible au moins.
Martin Harris
5
Je ne suis pas sûr que la vérification de A à z soit sûre, car elle apporte 6 caractères qui ne sont pas alphabétiques, dont un seul est souhaité (barre inférieure).
Steven Sudit
4
Concentrez-vous sur l'amélioration de la lisibilité de votre code. à moins que vous ne le fassiez en boucle comme 500 fois par seconde, l'efficacité n'est pas un gros problème. Utilisez une expression rationnelle et ce sera beaucoup plus facile à lire.l
Byron Whitlock
4
Byron, vous avez probablement raison de devoir mettre l'accent sur la lisibilité. Cependant, je suis sceptique quant à la lisibilité des expressions régulières. :-)
Steven Sudit
2
Les expressions régulières lisibles ou non sont un peu comme l'allemand étant lisible ou non; cela dépend si vous le savez ou non (bien que dans les deux cas, vous rencontrerez de temps en temps des règles grammaticales qui n'ont aucun sens;)
Blixt

Réponses:

325

Pourquoi pensez-vous que votre méthode n'est pas efficace? C'est en fait l'un des moyens les plus efficaces de le faire.

Vous devez bien sûr lire le caractère dans une variable locale ou utiliser un énumérateur pour réduire le nombre d'accès au tableau:

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

Une chose qui rend une méthode comme celle-ci efficace, c'est qu'elle évolue bien. Le temps d'exécution sera relatif à la longueur de la chaîne. Il n'y a pas de mauvaises surprises si vous l'utilisez sur une grosse chaîne.

Edit:
J'ai fait un test de performance rapide, exécutant chaque fonction un million de fois avec une chaîne de 24 caractères. Voici les résultats:

Fonction d'origine: 54,5 ms.
Ma modification suggérée: 47,1 ms.
Mine avec réglage de la capacité de StringBuilder: 43,3 ms.
Expression régulière: 294,4 ms.

Edit 2: J'ai ajouté la distinction entre AZ et az dans le code ci-dessus. (J'ai relancé le test de performance, et il n'y a pas de différence notable.)

Edit 3:
J'ai testé la solution lookup + char [], et elle fonctionne en 13 ms environ.

Le prix à payer est, bien sûr, l'initialisation de l'énorme table de recherche et sa conservation en mémoire. Eh bien, ce n'est pas tant de données, mais c'est beaucoup pour une fonction aussi triviale ...

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
la source
4
Je suis d'accord. La seule autre modification que je ferais serait d'ajouter l'argument de capacité initiale au constructeur StringBuilder, "= new StringBuilder (str.Length)".
David
2
Ma réponse, en utilisant un char[]tampon plutôt que StringBuilder, a un léger avantage sur celui-ci selon mes tests. (Le mien est cependant moins lisible, donc le petit avantage en
termes de
1
@Steven: C'est peut-être le cas, mais les repères parlent d'eux-mêmes! Dans mes tests, l'utilisation d'un char[]tampon fonctionne (légèrement) mieux que StringBuilder, même lors de la mise à l'échelle vers des chaînes de plusieurs dizaines de milliers de caractères.
LukeH
10
@downvoter: Pourquoi le downvote? Si vous n'expliquez pas ce que vous pensez être faux, cela ne peut pas améliorer la réponse.
Guffa
2
@SILENT: Non, ce n'est pas le cas, mais vous ne devriez le faire qu'une seule fois. Si vous allouez un tableau de cette taille à chaque fois que vous appelez la méthode (et si vous appelez la méthode fréquemment), la méthode devient de loin la plus lente et entraîne beaucoup de travail pour le garbage collector.
Guffa
195

Eh bien, à moins que vous n'ayez vraiment besoin de réduire les performances de votre fonction, optez simplement pour ce qui est le plus facile à maintenir et à comprendre. Une expression régulière ressemblerait à ceci:

Pour des performances supplémentaires, vous pouvez soit le précompiler, soit simplement lui demander de compiler au premier appel (les appels suivants seront plus rapides.)

public static string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}
Blixt
la source
1
Je suppose que c'est probablement une requête suffisamment complexe pour qu'elle soit plus rapide que l'approche du PO, surtout si elle est précompilée. Je n'ai cependant aucune preuve à l'appui de cela. Il doit être testé. À moins qu'elle ne soit considérablement plus lente, je choisirais cette approche, car elle est beaucoup plus facile à lire et à entretenir. +1
rmeador
6
C'est une regex très simple (pas de retour en arrière ou de trucs complexes là-dedans) donc ça devrait être sacrément rapide.
9
@rmeador: sans qu'il soit compilé, il est environ 5 fois plus lent, compilé, il est 3 fois plus lent que sa méthode. Encore 10x plus simple cependant :-D
user7116
6
Les expressions régulières ne sont pas des marteaux magiques et ne sont jamais plus rapides que du code optimisé à la main.
Christian Klauser
2
Pour ceux qui se souviennent de la célèbre citation de Knuth sur l'optimisation, c'est par où commencer. Ensuite, si vous trouvez que vous avez besoin du millième de milliseconde supplémentaire, optez pour l'une des autres techniques.
John
15

Je suggère de créer une table de recherche simple, que vous pouvez initialiser dans le constructeur statique pour définir une combinaison de caractères valide. Cela vous permet d'effectuer une vérification rapide et unique.

Éditer

De plus, pour la vitesse, vous voudrez initialiser la capacité de votre StringBuilder à la longueur de votre chaîne d'entrée. Cela évitera les réaffectations. Ensemble, ces deux méthodes vous donneront à la fois rapidité et flexibilité.

un autre montage

Je pense que le compilateur pourrait l'optimiser, mais pour une question de style et d'efficacité, je recommande foreach au lieu de for.

Steven Sudit
la source
Pour les tableaux, foret foreachproduisez un code similaire. Je ne connais pas les cordes cependant. Je doute que le JIT connaisse la nature de type tableau de String.
Christian Klauser
1
Je parie que le JIT en sait plus sur la nature de la chaîne de type tableau que votre [blague supprimée]. Anders etal a fait beaucoup de travail pour tout optimiser sur les chaînes en .net
J'ai fait cela en utilisant HashSet <char> et c'est environ 2x plus lent que sa méthode. Utiliser bool [] est à peine plus rapide (0.0469ms / iter v. 0.0559ms / iter) que la version qu'il a en OP ... avec le problème d'être moins lisible.
user7116
1
Je ne pouvais voir aucune différence de performances entre l'utilisation d'un tableau bool et un tableau int. J'utiliserais un tableau booléen, car il fait baisser la table de recherche de 256 ko à 64 ko, mais c'est encore beaucoup de données pour une fonction aussi triviale ... Et ce n'est qu'environ 30% plus rapide.
Guffa
1
@Guffa 2) Étant donné que nous ne conservons que des caractères alphanumériques et quelques caractères latins de base, nous n'avons besoin que d'une table pour l'octet faible, donc la taille n'est pas vraiment un problème. Si nous voulions être polyvalents, la technique Unicode standard est la double indirection. En d'autres termes, une table de 256 références de table, dont beaucoup pointent vers la même table vide.
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
la source
1
+1, testé et il est environ 40% plus rapide que StringBuilder. 0,0294 ms / chaîne v 0,0399 ms / chaîne
user7116
Pour être sûr, voulez-vous dire StringBuilder avec ou sans pré-allocation?
Steven Sudit
Avec la pré-allocation, elle est toujours 40% plus lente que l'allocation char [] et la nouvelle chaîne.
user7116
2
J'aime ça. J'ai modifié cette méthodeforeach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Chris Marisic
11

Une expression régulière ressemblera à:

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

Mais si les performances sont très importantes, je vous recommande de faire quelques benchmarks avant de sélectionner le "chemin regex" ...

CMS
la source
11

Si vous utilisez une liste dynamique de caractères, LINQ peut offrir une solution beaucoup plus rapide et élégante:

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

J'ai comparé cette approche à deux des approches "rapides" précédentes (compilation des versions):

  • Solution de baie de caractères par LukeH - 427 ms
  • Solution StringBuilder - 429 ms
  • LINQ (cette réponse) - 98 ms

Notez que l'algorithme est légèrement modifié - les caractères sont transmis sous forme de tableau plutôt que codés en dur, ce qui pourrait avoir un impact léger sur les choses (c'est-à-dire / les autres solutions auraient une boucle interne pour vérifier le tableau de caractères).

Si je passe à une solution codée en dur à l'aide d'une clause LINQ where, les résultats sont les suivants:

  • Solution de tableau de caractères - 7 ms
  • Solution StringBuilder - 22 ms
  • LINQ - 60 ms

Cela pourrait valoir la peine d'examiner LINQ ou une approche modifiée si vous envisagez d'écrire une solution plus générique, plutôt que de coder en dur la liste des caractères. LINQ vous donne certainement un code concis et très lisible - encore plus que Regex.

Chasseur d'ombre
la source
3
Cette approche a l'air sympa, mais elle ne fonctionne pas - Except () est une opération définie, donc vous ne vous retrouverez qu'avec la première apparition de chaque caractère unique dans la chaîne.
McKenzieG1
5

Je ne suis pas convaincu que votre algorithme soit tout sauf efficace. C'est O (n) et ne regarde chaque personnage qu'une seule fois. Vous n'allez pas faire mieux que ça, à moins que vous ne connaissiez par magie les valeurs avant de les vérifier.

Je voudrais cependant initialiser la capacité de votre StringBuilderà la taille initiale de la chaîne. Je suppose que votre problème de performance perçu provient de la réallocation de mémoire.

Note latérale: Vérifier A- zn'est pas sûr. Vous y compris [, \, ], ^, _et `...

Note latérale 2: Pour ce petit plus d'efficacité, mettez les comparaisons dans un ordre pour minimiser le nombre de comparaisons. (Au pire, vous parlez de 8 comparaisons, alors ne réfléchissez pas trop.) Cela change avec votre entrée attendue, mais un exemple pourrait être:

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

Note 3: Si pour une raison quelconque, vous avez VRAIMENT besoin que cela soit rapide, une déclaration de changement peut être plus rapide. Le compilateur doit créer une table de saut pour vous, résultant en une seule comparaison:

switch (str[i])
{
    case '0':
    case '1':
    .
    .
    .
    case '.':
        sb.Append(str[i]);
        break;
}
lc.
la source
1
Je suis d'accord que vous ne pouvez pas battre O (n) sur celui-ci. Cependant, il existe un coût par comparaison qui peut être abaissé. Une recherche de table a un coût fixe faible, tandis qu'une série de comparaisons va augmenter de coût à mesure que vous ajoutez d'autres exceptions.
Steven Sudit
À propos de la note 3, pensez-vous vraiment que la table de saut serait plus rapide que la recherche de table?
Steven Sudit
J'ai exécuté le test de performance rapide sur la solution de commutateur, et il effectue la même chose que la comparaison.
Guffa
@Steven Sudit - Je suppose qu'ils sont en fait à peu près les mêmes. Vous souhaitez effectuer un test?
lc.
7
La notation O (n) me fait parfois chier. Les gens feront des suppositions stupides basées sur le fait que l'algorithme est déjà O (n). Si nous avons changé cette routine pour remplacer les appels str [i] par une fonction qui a récupéré la valeur de comparaison en construisant une connexion SSL unique avec un serveur de l'autre côté du monde ... vous verriez sûrement une performance massive différence et l'algorithme est STILL O (n). Le coût de O (1) pour chaque algorithme est significatif et NON é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
la source
4

Vous pouvez utiliser une expression régulière comme suit:

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

Cela me semble bon. La seule amélioration que je ferais serait d'initialiser le StringBuilderavec la longueur de la chaîne.

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

Je suis d'accord avec cet exemple de code. La seule différence que je fais en méthode d'extension de type chaîne. Pour que vous puissiez l'utiliser dans une ligne ou un code très simple:

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

Merci à Guffa pour votre expérience.

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

J'utiliserais un remplacement de chaîne par une expression régulière recherchant des "caractères spéciaux", remplaçant tous les caractères trouvés par une chaîne vide.

Stephen Wrighton
la source
+1 certainement moins de code et sans doute plus lisible en ignorant Regex à écriture unique.
kenny
1
@kenny - Je suis d'accord. La question d'origine indique même que les chaînes sont courtes - 10-30 caractères. Mais apparemment, beaucoup de gens pensent toujours que nous vendons du temps CPU à la seconde ...
Tom Bushell
Reguler expressin fonctionne si paresseux, il ne devrait donc pas être utilisé toujours.
RockOnGom
2

J'ai dû faire quelque chose de similaire pour le travail, mais dans mon cas, j'ai dû filtrer tout ce qui n'est pas une lettre, un chiffre ou un espace (mais vous pouvez facilement le modifier selon vos besoins). Le filtrage se fait côté client en JavaScript, mais pour des raisons de sécurité je fais aussi le filtrage côté serveur. Comme je peux m'attendre à ce que la plupart des chaînes soient propres, je voudrais éviter de copier la chaîne à moins que j'en ai vraiment besoin. Cela me permet d'implémenter ci-dessous, qui devrait mieux fonctionner pour les chaînes propres et sales.

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

Pour les S&G, manière 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());

Cependant, je ne pense pas que ce sera le moyen le plus efficace.


la source
2
Ce n'est pas le cas, car c'est une recherche linéaire.
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
la source
1

Utilisation:

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
}

Et vous obtiendrez une chaîne propre s.

erase()le supprimera de tous les caractères spéciaux et est hautement personnalisable avec la my_predicate()fonction.

Bhavya Agarwal
la source
1

HashSet est O (1)
Je ne sais pas s'il est plus rapide que la comparaison existante

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

J'ai testé et ce pas plus vite que la réponse acceptée.
Je vais laisser comme si vous aviez besoin d'un jeu de caractères configurable, ce serait une bonne solution.

paparazzo
la source
Pourquoi pensez-vous que la comparaison n'est pas O (1)?
Guffa
@Guffa Je ne suis pas sûr que ce ne soit pas le cas et j'ai supprimé mon commentaire. Et +1. J'aurais dû faire plus de tests avant de faire le commentaire.
paparazzo
1

Je me demande si un remplacement basé sur Regex (éventuellement compilé) est plus rapide. Devrait tester cela faudrait quelqu'un vérifie que cela est environ 5 fois plus lent.

En dehors de cela, vous devez initialiser le StringBuilder avec une longueur attendue, afin que la chaîne intermédiaire ne doive pas être copiée pendant sa croissance.

Un bon nombre est la longueur de la chaîne d'origine, ou quelque chose de légèrement inférieur (selon la nature des entrées de fonctions).

Enfin, vous pouvez utiliser une table de recherche (dans la plage 0..127) pour savoir si un caractère doit être accepté.

Christian Klauser
la source
Une expression régulière a déjà été testée et elle est environ cinq fois plus lente. Avec une table de recherche dans la plage 0..127, vous devez toujours vérifier le code de caractères avant d'utiliser la table de recherche, car les caractères sont des valeurs de 16 bits, et non des valeurs de 7 bits.
Guffa
@Guffa Euh ... oui? ;)
Christian Klauser
1

Le code suivant a la sortie suivante (la conclusion est que nous pouvons également économiser des ressources mémoire en allouant une taille plus petite au tableau):

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  

Vous pouvez également ajouter les lignes de code suivantes pour prendre en charge les paramètres régionaux russes (la taille du tableau sera 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
la source
1

Je ne suis pas sûr que ce soit le moyen le plus efficace, mais ça marche pour moi

 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
la source
La réponse ne travail, mais la question était pour C #. (PS: je sais que c'était il y a pratiquement cinq ans, mais quand même ..) J'ai utilisé le convertisseur Telerik VB en C #, (et vice-versa) et le code fonctionnait très bien - je ne suis sûr de personne d'autre cependant. (Autre chose, converter.telerik.com )
Momoro
1

Il y a beaucoup de solutions proposées ici, certaines plus efficaces que d'autres, mais peut-être pas très lisibles. En voici une qui n'est peut-être pas la plus efficace, mais certainement utilisable dans la plupart des situations, et qui est assez concise et lisible, tirant parti de 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
la source
-1
public static string RemoveSpecialCharacters(string str){
    return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
}
Jawaid
la source
1
Je crains que ce replaceAllne soit pas une fonction de chaîne C # mais 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
la source
La réponse est fausse. Si vous allez utiliser l'expression régulière, elle devrait être inclusive, et non exclusive, car certains personnages vous manquent maintenant. En fait, il y a déjà une réponse avec regex. Et pour être complet - l'expression régulière est plus lente que la fonction de comparaison directe des caractères.
TPAKTOPA
-3

Si vous vous inquiétez de la vitesse, utilisez des pointeurs pour modifier la chaîne existante. Vous pouvez épingler la chaîne et obtenir un pointeur dessus, puis exécuter une boucle for sur chaque caractère, en remplaçant chaque caractère non valide par un caractère de remplacement. Il serait extrêmement efficace et ne nécessiterait pas d'allouer de nouvelle mémoire de chaîne. Vous devrez également compiler votre module avec l'option unsafe et ajouter le modificateur "unsafe" à votre en-tête de méthode afin d'utiliser des pointeurs.

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
la source
14
Noooooooooo! Changer une chaîne dans .NET est BAAAAAAAAAAAAD! Tout dans le cadre repose sur la règle selon laquelle les cordes sont immuables, et si vous cassez cela, vous pouvez obtenir des effets secondaires très surprenants ...
Guffa