Existe-t-il une alternative à ssortingng.Replace qui est insensible à la casse?

Je dois rechercher une chaîne et remplacer toutes les occurrences de %FirstName% et %PolicyAmount% par une valeur extraite d’une firebase database. Le problème est que la capitalisation de FirstName varie. Cela m’empêche d’utiliser la méthode Ssortingng.Replace() . J’ai vu des pages Web sur le sujet qui suggèrent

 Regex.Replace(strInput, strToken, strReplaceWith, RegexOptions.IgnoreCase); 

Cependant, pour une raison quelconque, lorsque j’essaie de remplacer %PolicyAmount% par $0 , le remplacement n’a jamais lieu. Je suppose que cela a quelque chose à voir avec le signe dollar étant un caractère réservé dans regex.

Existe-t-il une autre méthode que je peux utiliser qui ne nécessite pas de nettoyer les entrées pour traiter les caractères spéciaux des expressions rationnelles?

À partir de MSDN
$ 0 – “Remplace la dernière sous-chaîne par le numéro de groupe (décimal).”

Dans les expressions régulières .NET, le groupe 0 est toujours la totalité de la correspondance. Pour un littéral $, vous devez

 ssortingng value = Regex.Replace("%PolicyAmount%", "%PolicyAmount%", @"$$0", RegexOptions.IgnoreCase); 

Semble que ssortingng.Replace devrait avoir une surcharge qui prend un argument SsortingngComparison . Comme ce n’est pas le cas, vous pouvez essayer quelque chose comme ceci:

 public static ssortingng ReplaceSsortingng(ssortingng str, ssortingng oldValue, ssortingng newValue, SsortingngComparison comparison) { SsortingngBuilder sb = new SsortingngBuilder(); int previousIndex = 0; int index = str.IndexOf(oldValue, comparison); while (index != -1) { sb.Append(str.Subssortingng(previousIndex, index - previousIndex)); sb.Append(newValue); index += oldValue.Length; previousIndex = index; index = str.IndexOf(oldValue, index, comparison); } sb.Append(str.Subssortingng(previousIndex)); return sb.ToSsortingng(); } 

Genre d’un groupe de réponses déroutant, en partie parce que le titre de la question est en réalité beaucoup plus large que la question spécifique posée. Après avoir lu, je ne suis pas sûr qu’il y ait quelques modifications à apporter à l’assimilation de toutes les bonnes choses, alors j’ai pensé que j’essaierais de faire la sum.

Voici une méthode d’extension qui, à mon avis, évite les pièges mentionnés ici et fournit la solution la plus largement applicable.

 public static ssortingng ReplaceCaseInsensitiveFind(this ssortingng str, ssortingng findMe, ssortingng newValue) { return Regex.Replace(str, Regex.Escape(findMe), Regex.Replace(newValue, "\\$[0-9]+", @"$$$0"), RegexOptions.IgnoreCase); } 

Alors…

  • Ceci est une méthode d’extension @ MarkRobinson
  • Cela n’essaie pas d’ignorer Regex @Helge (vous devez vraiment faire byte-by-byte si vous voulez enchaîner sniff comme ça en dehors de Regex)
  • Passe l’ excellent cas de test de @MichaelLiu, "œ".ReplaceCaseInsensitiveFind("oe", "") , bien qu’il ait pu avoir un comportement légèrement différent en tête.

Malheureusement, le commentaire de @HA que vous devez Escape tous les trois n’est pas correct . La valeur initiale et newValue n’ont pas besoin d’être.

Remarque: Cependant, vous devez échapper à $ s dans la nouvelle valeur que vous insérez si elles font partie de ce qui semble être un marqueur “valeur capturée” . Ainsi, les trois dollars signés dans le Regex.Replace dans Regex.Replace [sic]. Sans cela, quelque chose comme ça casse …

"This is HIS fork, hIs spoon, hissssssss knife.".ReplaceCaseInsensitiveFind("his", @"he$0r")

Voici l’erreur:

 An unhandled exception of type 'System.ArgumentException' occurred in System.dll Additional information: parsing "The\hisr\ is\ he\HISr\ fork,\ he\hIsr\ spoon,\ he\hisrsssssss\ knife\." - Unrecognized escape sequence \h. 

Dites-moi quoi, je sais que les gens qui sont à l’aise avec Regex ont l’impression que leur utilisation évite les erreurs, mais je suis souvent toujours enclin à protéger les chaînes (mais seulement après avoir lu Spolsky sur les encodages ) pour être sûr d’avoir ce que vous voulez. destiné à des cas d’utilisation importants. Cela me rappelle un peu Crockford sur les ” expressions régulières non sécurisées “. Nous écrivons trop souvent des regexps qui autorisent ce que nous voulons (si nous sums chanceux), mais autorisons involontairement plus dans (par exemple, est-ce que $10 vraiment une chaîne de “valeur de capture” valide dans ma regexp newValue, ci-dessus?) assez. Les deux méthodes ont une valeur, et toutes deux encouragent différents types d’erreurs involontaires. Il est souvent facile de sous-estimer la complexité.

Ce bizarre $ escaping (et que Regex.Escape n’a pas échappé aux modèles de valeurs capturées comme $0 comme je l’aurais prévu dans les valeurs de remplacement) m’a rendu fou pendant un moment. La programmation est difficile (c) 1842

Voici une méthode d’extension. Je ne sais pas où je l’ai trouvé.

 public static class SsortingngExtensions { public static ssortingng Replace(this ssortingng originalSsortingng, ssortingng oldValue, ssortingng newValue, SsortingngComparison comparisonType) { int startIndex = 0; while (true) { startIndex = originalSsortingng.IndexOf(oldValue, startIndex, comparisonType); if (startIndex == -1) break; originalSsortingng = originalSsortingng.Subssortingng(0, startIndex) + newValue + originalSsortingng.Subssortingng(startIndex + oldValue.Length); startIndex += newValue.Length; } return originalSsortingng; } } 

La méthode la plus simple consiste simplement à utiliser la méthode Replace qui est fournie avec .Net et qui existe depuis .Net 1.0:

 ssortingng res = Microsoft.VisualBasic.Ssortingngs.Replace(res, "%PolicyAmount%", "$0", Compare: Microsoft.VisualBasic.CompareMethod.Text); 

Pour utiliser cette méthode, vous devez append une référence à l’assemblage Microsoft.VisualBasic. Cet assemblage est une partie standard du runtime .Net, ce n’est pas un téléchargement supplémentaire ou marqué comme obsolète.

  ///  /// A case insenstive replace function. ///  /// The ssortingng to examine.(HayStack) /// The value to replace.(Needle) /// The new value to be inserted /// A ssortingng public static ssortingng CaseInsenstiveReplace(ssortingng originalSsortingng, ssortingng oldValue, ssortingng newValue) { Regex regEx = new Regex(oldValue, RegexOptions.IgnoreCase | RegexOptions.Multiline); return regEx.Replace(originalSsortingng, newValue); } 

Inspiré par la réponse de cfeduke, j’ai créé cette fonction qui utilise IndexOf pour trouver l’ancienne valeur dans la chaîne, puis la remplace par la nouvelle valeur. Je l’ai utilisé dans un script SSIS traitant des millions de lignes, et la méthode regex était beaucoup plus lente.

 public static ssortingng ReplaceCaseInsensitive(this ssortingng str, ssortingng oldValue, ssortingng newValue) { int prevPos = 0; ssortingng retval = str; // find the first occurence of oldValue int pos = retval.IndexOf(oldValue, SsortingngComparison.InvariantCultureIgnoreCase); while (pos > -1) { // remove oldValue from the ssortingng retval = retval.Remove(pos, oldValue.Length); // insert newValue in it's place retval = retval.Insert(pos, newValue); // check if oldValue is found further down prevPos = pos + newValue.Length; pos = retval.IndexOf(oldValue, prevPos, SsortingngComparison.InvariantCultureIgnoreCase); } return retval; } 

Développez la réponse populaire de C. Dragon 76 en transformant son code en une extension qui surcharge la méthode Replace par défaut.

 public static class SsortingngExtensions { public static ssortingng Replace(this ssortingng str, ssortingng oldValue, ssortingng newValue, SsortingngComparison comparison) { SsortingngBuilder sb = new SsortingngBuilder(); int previousIndex = 0; int index = str.IndexOf(oldValue, comparison); while (index != -1) { sb.Append(str.Subssortingng(previousIndex, index - previousIndex)); sb.Append(newValue); index += oldValue.Length; previousIndex = index; index = str.IndexOf(oldValue, index, comparison); } sb.Append(str.Subssortingng(previousIndex)); return sb.ToSsortingng(); } } 

Basé sur la réponse de Jeff Reddy, avec quelques optimisations et validations:

 public static ssortingng Replace(ssortingng str, ssortingng oldValue, ssortingng newValue, SsortingngComparison comparison) { if (oldValue == null) throw new ArgumentNullException("oldValue"); if (oldValue.Length == 0) throw new ArgumentException("Ssortingng cannot be of zero length.", "oldValue"); SsortingngBuilder sb = null; int startIndex = 0; int foundIndex = str.IndexOf(oldValue, comparison); while (foundIndex != -1) { if (sb == null) sb = new SsortingngBuilder(str.Length + (newValue != null ? Math.Max(0, 5 * (newValue.Length - oldValue.Length)) : 0)); sb.Append(str, startIndex, foundIndex - startIndex); sb.Append(newValue); startIndex = foundIndex + oldValue.Length; foundIndex = str.IndexOf(oldValue, startIndex, comparison); } if (startIndex == 0) return str; sb.Append(str, startIndex, str.Length - startIndex); return sb.ToSsortingng(); } 

une version similaire à celle de C. Dragon, mais si vous n’avez besoin que d’un seul remplacement:

 int n = myText.IndexOf(oldValue, System.SsortingngComparison.InvariantCultureIgnoreCase); if (n >= 0) { myText = myText.Subssortingng(0, n) + newValue + myText.Subssortingng(n + oldValue.Length); } 

Voici une autre option pour exécuter des remplacements Regex, car peu de personnes semblent remarquer que les correspondances contiennent l’emplacement dans la chaîne:

  public static ssortingng ReplaceCaseInsensative( this ssortingng s, ssortingng oldValue, ssortingng newValue ) { var sb = new SsortingngBuilder(s); int offset = oldValue.Length - newValue.Length; int matchNo = 0; foreach (Match match in Regex.Matches(s, Regex.Escape(oldValue), RegexOptions.IgnoreCase)) { sb.Remove(match.Index - (offset * matchNo), match.Length).Insert(match.Index - (offset * matchNo), newValue); matchNo++; } return sb.ToSsortingng(); } 
 Regex.Replace(strInput, strToken.Replace("$", "[$]"), strReplaceWith, RegexOptions.IgnoreCase); 

La méthode d’expression régulière devrait fonctionner. Cependant, ce que vous pouvez également faire est de réduire la chaîne de la firebase database en minuscule, de réduire la valeur en% des variables% que vous avez, puis de localiser les positions et les longueurs dans la chaîne inférieure de la firebase database. Rappelez-vous que les positions dans une chaîne ne changent pas simplement parce que son boîtier inférieur.

Ensuite, en utilisant une boucle inversée (il est plus facile, si vous ne le faites pas, vous devez tenir compte du déplacement des points ultérieurs) en retirant de votre firebase database les% variables% de leur position et longueur et insérer les valeurs de remplacement.

(Puisque tout le monde prend un coup à cela). Voici ma version (avec des vérifications nulles et une saisie correcte des entrées et des remplacements) ** Inspiré d’Internet et d’autres versions:

 using System; using System.Text.RegularExpressions; public static class MyExtensions { public static ssortingng ReplaceIgnoreCase(this ssortingng search, ssortingng find, ssortingng replace) { return Regex.Replace(search ?? "", Regex.Escape(find ?? ""), (replace ?? "").Replace("$", "$$"), RegexOptions.IgnoreCase); } } 

Usage:

 var result = "This is a test".ReplaceIgnoreCase("IS", "was"); 

Laissez-moi faire mon cas et vous pourrez alors me déchirer si vous voulez.

Regex n’est pas la solution à ce problème – trop lent et gourmand en mémoire, relativement parlant.

SsortingngBuilder est bien meilleur que la manipulation de chaînes.

Comme il s’agira d’une méthode d’extension pour compléter ssortingng.Replace , je pense qu’il est important de trouver comment cela fonctionne – par conséquent, il est important de ssortingng.Replace exceptions pour les mêmes problèmes d’argument, car la chaîne d’origine doit être remplacée.

Je pense qu’avoir un paramètre SsortingngComparison n’est pas une bonne idée. Je l’ai essayé, mais le cas de test initialement mentionné par Michael-Liu a montré un problème: –

 [TestCase("œ", "oe", "", SsortingngComparison.InvariantCultureIgnoreCase, Result = "")] 

Bien que IndexOf corresponde, il existe une incohérence entre la longueur de la correspondance dans la chaîne source (1) et oldValue.Length (2). Cela s’est manifesté en provoquant IndexOutOfRange dans d’autres solutions lorsque oldValue.Length a été ajouté à la position de correspondance actuelle et que je n’ai pas pu trouver un moyen de contourner ce problème. Regex ne parvient pas à faire correspondre la casse, alors j’ai pris la solution pragmatique SsortingngComparison.OrdinalIgnoreCase utiliser uniquement SsortingngComparison.OrdinalIgnoreCase pour ma solution.

Mon code est similaire aux autres réponses, mais je pense que je cherche une correspondance avant de créer un SsortingngBuilder . Si aucun n’est trouvé, une allocation potentiellement importante est évitée. Le code devient alors un do{...}while plutôt qu’un while{...}

J’ai effectué des tests approfondis par rapport à d’autres réponses, ce qui s’est traduit par une rapidité et une utilisation légèrement moins importantes de la mémoire.

  public static ssortingng ReplaceCaseInsensitive(this ssortingng str, ssortingng oldValue, ssortingng newValue) { if (str == null) throw new ArgumentNullException(nameof(str)); if (oldValue == null) throw new ArgumentNullException(nameof(oldValue)); if (oldValue.Length == 0) throw new ArgumentException("Ssortingng cannot be of zero length.", nameof(oldValue)); var position = str.IndexOf(oldValue, 0, SsortingngComparison.OrdinalIgnoreCase); if (position == -1) return str; var sb = new SsortingngBuilder(str.Length); var lastPosition = 0; do { sb.Append(str, lastPosition, position - lastPosition); sb.Append(newValue); } while ((position = str.IndexOf(oldValue, lastPosition = position + oldValue.Length, SsortingngComparison.OrdinalIgnoreCase)) != -1); sb.Append(str, lastPosition, str.Length - lastPosition); return sb.ToSsortingng(); }