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.
Réponses:
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:
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 ...
la source
char[]
tampon plutôt queStringBuilder
, a un léger avantage sur celui-ci selon mes tests. (Le mien est cependant moins lisible, donc le petit avantage enchar[]
tampon fonctionne (légèrement) mieux queStringBuilder
, même lors de la mise à l'échelle vers des chaînes de plusieurs dizaines de milliers de caractères.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.)
la source
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.
la source
for
etforeach
produisez un code similaire. Je ne connais pas les cordes cependant. Je doute que le JIT connaisse la nature de type tableau de String.la source
foreach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Une expression régulière ressemblera à:
Mais si les performances sont très importantes, je vous recommande de faire quelques benchmarks avant de sélectionner le "chemin regex" ...
la source
Si vous utilisez une liste dynamique de caractères, LINQ peut offrir une solution beaucoup plus rapide et élégante:
J'ai comparé cette approche à deux des approches "rapides" précédentes (compilation des versions):
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:
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.
la source
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
-z
n'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:
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:
la source
la source
Vous pouvez utiliser une expression régulière comme suit:
la source
Cela me semble bon. La seule amélioration que je ferais serait d'initialiser le
StringBuilder
avec la longueur de la chaîne.la source
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:
Merci à Guffa pour votre expérience.
la source
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.
la source
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.
la source
Pour les S&G, manière Linq-ified:
Cependant, je ne pense pas que ce sera le moyen le plus efficace.
la source
la source
Utilisation:
Et vous obtiendrez une chaîne propre
s
.erase()
le supprimera de tous les caractères spéciaux et est hautement personnalisable avec lamy_predicate()
fonction.la source
HashSet est O (1)
Je ne sais pas s'il est plus rapide que la comparaison existante
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.
la source
Je me demande si un remplacement basé sur Regex (éventuellement compilé) est plus rapide.
Devrait tester celafaudrait 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é.
la source
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):
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):
la source
Je ne suis pas sûr que ce soit le moyen le plus efficace, mais ça marche pour moi
la source
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:
la source
la source
replaceAll
ne soit pas une fonction de chaîne C # mais Java ou JavaScriptla source
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.
la source