Un moyen plus rapide de remplacer plusieurs chaînes

Je dois faire ce qui suit:

static ssortingng[] pats = { "å", "Å", "æ", "Æ", "ä", "Ä", "ö", "Ö", "ø", "Ø" ,"è", "È", "à", "À", "ì", "Ì", "õ", "Õ", "ï", "Ï" }; static ssortingng[] repl = { "a", "A", "a", "A", "a", "A", "o", "O", "o", "O", "e", "E", "a", "A", "i", "I", "o", "O", "i", "I" }; static int i = pats.Length; int j; // function for the replacement(s) public ssortingng DoRepl(ssortingng Inp) { ssortingng tmp = Inp; for( j = 0; j < i; j++ ) { tmp = Regex.Replace(tmp,pats[j],repl[j]); } return tmp.ToString(); } /* Main flow processes about 45000 lines of input */ 

Chaque ligne a 6 éléments qui passent par DoRepl. Environ 300 000 appels de fonction. Chacun fait 20 Regex.Replace, totalisant environ 6 millions de remplacements.

Y a-t-il un moyen plus élégant de faire cela en moins de passes?

 static Dictionary repl = new Dictionary() { { 'å', 'a' }, { 'ø', 'o' } }; // etc... public ssortingng DoRepl(ssortingng Inp) { var tmp = Inp.Select(c => { char r; if (repl.TryGetValue(c, out r)) return r; return c; }); return new ssortingng(tmp.ToArray()); } 

Chaque caractère est vérifié une seule fois par rapport à un dictionnaire et remplacé s’il est trouvé dans le dictionnaire.

Que diriez-vous de ce “truc”?

 ssortingng conv = Encoding.ASCII.GetSsortingng(Encoding.GetEncoding("Cyrillic").GetBytes(input)); 

Sans regex, cela pourrait être beaucoup plus rapide.

  for( j = 0; j < i; j++ ) { tmp = tmp.Replace(pats[j], repl[j]); } 

modifier

Une autre façon en utilisant Zip et un SsortingngBuilder :

 SsortingngBuilder result = new SsortingngBuilder(input); foreach (var zipped = patterns.Zip(replacements, (p, r) => new {p, r})) { result = result.Replace(zipped.p, zipped.r); } return result.ToSsortingng(); 

Premièrement, j’utiliserais un SsortingngBuilder pour effectuer la traduction dans un tampon et éviter de créer de nouvelles chaînes partout.

Ensuite, idéalement, nous aimerions quelque chose qui ressemble à XPath ‘ translate() , afin de pouvoir utiliser des chaînes plutôt que des tableaux ou des mappages. Faisons cela dans une méthode d’extension :

 public static SsortingngBuilder Translate(this SsortingngBuilder builder, ssortingng inChars, ssortingng outChars) { int length = Math.Min(inChars.Length, outChars.Length); for (int i = 0; i < length; ++i) { builder.Replace(inChars[i], outChars[i]); } return builder; } 

Alors utilisez-le:

 SsortingngBuilder builder = new SsortingngBuilder(yourSsortingng); yourSsortingng = builder.Translate("åÅæÆäÄöÖøØèÈàÀìÌõÕïÏ", "aAaAaAoOoOeEaAiIoOiI").ToSsortingng(); 

Le problème avec votre regex originale est que vous ne l’utilisez pas à son plein potentiel. N’oubliez pas qu’un motif de regex peut avoir des alternances. Vous aurez toujours besoin d’un dictionnaire, mais vous pouvez le faire en un seul passage sans passer en boucle à travers chaque caractère.

Ceci serait réalisé comme suit:

 ssortingng[] pats = { "å", "Å", "æ", "Æ", "ä", "Ä", "ö", "Ö", "ø", "Ø" ,"è", "È", "à", "À", "ì", "Ì", "õ", "Õ", "ï", "Ï" }; ssortingng[] repl = { "a", "A", "a", "A", "a", "A", "o", "O", "o", "O", "e", "E", "a", "A", "i", "I", "o", "O", "i", "I" }; // using Zip as a shortcut, otherwise setup dictionary differently as others have shown var dict = pats.Zip(repl, (k,v) => new { Key = k, Value = v }).ToDictionary(o => o.Key, o => o.Value); ssortingng input = "åÅæÆäÄöÖøØèÈàÀìÌõÕïÏ"; ssortingng pattern = Ssortingng.Join("|", dict.Keys.Select(k => k)); // use ToArray() for .NET 3.5 ssortingng result = Regex.Replace(input, pattern, m => dict[m.Value]); Console.WriteLine("Pattern: " + pattern); Console.WriteLine("Input: " + input); Console.WriteLine("Result: " + result); 

Bien sûr, vous devriez toujours échapper à votre modèle en utilisant Regex.Escape . Dans ce cas, cela n’est pas nécessaire puisque nous connaissons l’ensemble fini de caractères et qu’ils n’ont pas besoin d’être échappés.

Si vous souhaitez supprimer les accents, alors cette solution serait peut-être utile. Comment puis-je supprimer les diacritiques (accents) d’une chaîne dans .NET?

Sinon, je voudrais ceci en un seul passage:

 Dictionary replacements = new Dictionary(); ... SsortingngBuilder result = new SsortingngBuilder(); foreach(char c in str) { char rc; if (!_replacements.TryGetValue(c, out rc) { rc = c; } result.Append(rc); } 

Le moyen le plus rapide (à mon humble avis (comparé même avec le dictionnaire) dans le cas particulier du remplacement de un à un serait une carte de caractère complète:

 public class Converter { private readonly char[] _map; public Converter() { // This code assumes char to be a short unsigned integer _map = new char[char.MaxValue]; for (int i = 0; i < _map.Length; i++) _map[i] = (char)i; _map['å'] = 'a'; // Note that 'å' is used as an integer index into the array. _map['Å'] = 'A'; _map['æ'] = 'a'; // ... the rest of overriding map } public string Convert(string source) { if (string.IsNullOrEmpty(source)) return source; var result = new char[source.Length]; for (int i = 0; i < source.Length; i++) result[i] = _map[source[i]]; // convert using the map return new string(result); } } 

Pour accélérer davantage ce code, vous pouvez utiliser le mot-clé "unsafe" et utiliser des pointeurs. De cette façon, parcourir le tableau de chaînes pourrait être fait plus rapidement et sans vérification liée (ce qui en théorie serait optimisé par la VM, mais pourrait ne pas l'être).

Je ne connais pas bien la classe Regex, mais la plupart des moteurs d’expression régulière ont une opération de translittération qui fonctionnerait bien ici. Ensuite, vous n’auriez besoin que d’un appel par ligne.