ご意見をお寄せいただきありがとうございます。あなたの入力をテストするために、簡単で汚いベンチマークを書きました。500.000 回の繰り返しで 4 つの文字列の解析をテストし、4 つのパスを実行しました。結果は次のとおりです。
*** パス 1
古い (Chained String.Replace()) 方法は 814 ミリ秒で完了
logicnp (ToCharArray) の方法が 916 ミリ秒で完了
oleksii (StringBuilder) の方法は 943 ミリ秒で完了しました
André Christoffer Andersen (Lambda w/ Aggregate) の方法は 2551 ミリ秒で完了しました
Richard (MatchEvaluator を使用した正規表現) の方法は 215 ミリ秒で完了しました
Marc Gravell (静的正規表現) の方法は 1008 ミリ秒で完了しました
*** パス 2
古い (Chained String.Replace()) 方法は 786 ミリ秒で完了
logicnp (ToCharArray) の方法が 920 ミリ秒で完了
oleksii (StringBuilder) の方法が 905 ミリ秒で完了
André Christoffer Andersen (Lambda w/ Aggregate) の方法は 2515 ミリ秒で完了しました
Richard (MatchEvaluator を使用した正規表現) の方法は 217 ミリ秒で完了しました
Marc Gravell (静的正規表現) の方法は 1025 ミリ秒で完了しました
*** パス 3
古い (Chained String.Replace()) 方法は 775 ミリ秒で完了しました
logicnp (ToCharArray) の方法が 903 ミリ秒で完了
oleksii (StringBuilder) の方法が 931 ミリ秒で完了
André Christoffer Andersen (Lambda w/ Aggregate) の方法は 2529 ミリ秒で完了しました
Richard (MatchEvaluator を使用した正規表現) の方法は 214 ミリ秒で完了しました
Marc Gravell (静的正規表現) の方法は 1022 ミリ秒で完了しました
*** パス 4
古い (Chained String.Replace()) 方法は 799 ミリ秒で完了
logicnp (ToCharArray) の方法は 908 ミリ秒で完了
oleksii (StringBuilder) の方法は 938 ミリ秒で完了しました
André Christoffer Andersen (Lambda w/ Aggregate) の方法は 2592 ミリ秒で完了しました
Richard (MatchEvaluator を使用した正規表現) の方法は 225 ミリ秒で完了しました
Marc Gravell (静的正規表現) の方法は 1050 ミリ秒で完了しました
このベンチマークのコードは次のとおりです。コードを見直して、@Richard が最速の方法であることを確認してください。出力が正しいかどうかを確認していないことに注意してください。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Text.RegularExpressions;
namespace StringReplaceTest
{
class Program
{
static string test1 = "A^@[BCD";
static string test2 = "E]FGH\\";
static string test3 = "ijk`l}m";
static string test4 = "nopq~{r";
static readonly Dictionary<char, string> repl =
new Dictionary<char, string>
{
{'^', "Č"}, {'@', "Ž"}, {'[', "Š"}, {']', "Ć"}, {'`', "ž"}, {'}', "ć"}, {'~', "č"}, {'{', "š"}, {'\\', "Đ"}
};
static readonly Regex replaceRegex;
static Program() // static initializer
{
StringBuilder pattern = new StringBuilder().Append('[');
foreach (var key in repl.Keys)
pattern.Append(Regex.Escape(key.ToString()));
pattern.Append(']');
replaceRegex = new Regex(pattern.ToString(), RegexOptions.Compiled);
}
public static string Sanitize(string input)
{
return replaceRegex.Replace(input, match =>
{
return repl[match.Value[0]];
});
}
static string DoGeneralReplace(string input)
{
var sb = new StringBuilder(input);
return sb.Replace('^', 'Č').Replace('@', 'Ž').Replace('[', 'Š').Replace(']', 'Ć').Replace('`', 'ž').Replace('}', 'ć').Replace('~', 'č').Replace('{', 'š').Replace('\\', 'Đ').ToString();
}
//Method for replacing chars with a mapping
static string Replace(string input, IDictionary<char, char> replacementMap)
{
return replacementMap.Keys
.Aggregate(input, (current, oldChar)
=> current.Replace(oldChar, replacementMap[oldChar]));
}
static void Main(string[] args)
{
for (int i = 1; i < 5; i++)
DoIt(i);
}
static void DoIt(int n)
{
Stopwatch sw = new Stopwatch();
int idx = 0;
Console.WriteLine("*** Pass " + n.ToString());
// old way
sw.Start();
for (idx = 0; idx < 500000; idx++)
{
string result1 = test1.Replace('^', 'Č').Replace('@', 'Ž').Replace('[', 'Š').Replace(']', 'Ć').Replace('`', 'ž').Replace('}', 'ć').Replace('~', 'č').Replace('{', 'š').Replace('\\', 'Đ');
string result2 = test2.Replace('^', 'Č').Replace('@', 'Ž').Replace('[', 'Š').Replace(']', 'Ć').Replace('`', 'ž').Replace('}', 'ć').Replace('~', 'č').Replace('{', 'š').Replace('\\', 'Đ');
string result3 = test3.Replace('^', 'Č').Replace('@', 'Ž').Replace('[', 'Š').Replace(']', 'Ć').Replace('`', 'ž').Replace('}', 'ć').Replace('~', 'č').Replace('{', 'š').Replace('\\', 'Đ');
string result4 = test4.Replace('^', 'Č').Replace('@', 'Ž').Replace('[', 'Š').Replace(']', 'Ć').Replace('`', 'ž').Replace('}', 'ć').Replace('~', 'č').Replace('{', 'š').Replace('\\', 'Đ');
}
sw.Stop();
Console.WriteLine("Old (Chained String.Replace()) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms");
Dictionary<char, char> replacements = new Dictionary<char, char>();
replacements.Add('^', 'Č');
replacements.Add('@', 'Ž');
replacements.Add('[', 'Š');
replacements.Add(']', 'Ć');
replacements.Add('`', 'ž');
replacements.Add('}', 'ć');
replacements.Add('~', 'č');
replacements.Add('{', 'š');
replacements.Add('\\', 'Đ');
// logicnp way
sw.Reset();
sw.Start();
for (idx = 0; idx < 500000; idx++)
{
char[] charArray1 = test1.ToCharArray();
for (int i = 0; i < charArray1.Length; i++)
{
char newChar;
if (replacements.TryGetValue(test1[i], out newChar))
charArray1[i] = newChar;
}
string result1 = new string(charArray1);
char[] charArray2 = test2.ToCharArray();
for (int i = 0; i < charArray2.Length; i++)
{
char newChar;
if (replacements.TryGetValue(test2[i], out newChar))
charArray2[i] = newChar;
}
string result2 = new string(charArray2);
char[] charArray3 = test3.ToCharArray();
for (int i = 0; i < charArray3.Length; i++)
{
char newChar;
if (replacements.TryGetValue(test3[i], out newChar))
charArray3[i] = newChar;
}
string result3 = new string(charArray3);
char[] charArray4 = test4.ToCharArray();
for (int i = 0; i < charArray4.Length; i++)
{
char newChar;
if (replacements.TryGetValue(test4[i], out newChar))
charArray4[i] = newChar;
}
string result4 = new string(charArray4);
}
sw.Stop();
Console.WriteLine("logicnp (ToCharArray) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms");
// oleksii way
sw.Reset();
sw.Start();
for (idx = 0; idx < 500000; idx++)
{
string result1 = DoGeneralReplace(test1);
string result2 = DoGeneralReplace(test2);
string result3 = DoGeneralReplace(test3);
string result4 = DoGeneralReplace(test4);
}
sw.Stop();
Console.WriteLine("oleksii (StringBuilder) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms");
// André Christoffer Andersen way
sw.Reset();
sw.Start();
for (idx = 0; idx < 500000; idx++)
{
string result1 = Replace(test1, replacements);
string result2 = Replace(test2, replacements);
string result3 = Replace(test3, replacements);
string result4 = Replace(test4, replacements);
}
sw.Stop();
Console.WriteLine("André Christoffer Andersen (Lambda w/ Aggregate) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms");
// Richard way
sw.Reset();
sw.Start();
Regex reg = new Regex(@"\^|@|\[|\]|`|\}|~|\{|\\");
MatchEvaluator eval = match =>
{
switch (match.Value)
{
case "^": return "Č";
case "@": return "Ž";
case "[": return "Š";
case "]": return "Ć";
case "`": return "ž";
case "}": return "ć";
case "~": return "č";
case "{": return "š";
case "\\": return "Đ";
default: throw new Exception("Unexpected match!");
}
};
for (idx = 0; idx < 500000; idx++)
{
string result1 = reg.Replace(test1, eval);
string result2 = reg.Replace(test2, eval);
string result3 = reg.Replace(test3, eval);
string result4 = reg.Replace(test4, eval);
}
sw.Stop();
Console.WriteLine("Richard (Regex w/ MatchEvaluator) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms");
// Marc Gravell way
sw.Reset();
sw.Start();
for (idx = 0; idx < 500000; idx++)
{
string result1 = Sanitize(test1);
string result2 = Sanitize(test2);
string result3 = Sanitize(test3);
string result4 = Sanitize(test4);
}
sw.Stop();
Console.WriteLine("Marc Gravell (Static Regex) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms\n");
}
}
}
編集 2020 年 6 月
この Q&A はまだヒットしているため、今回は .NET Core 3.1 を使用してコンパイルされた IndexOfAny を使用して StringBuilder を使用して、user1664043 からの追加の入力で更新したいと思いました。結果は次のとおりです。
*** パス 1
古い (Chained String.Replace()) 方法は 199 ミリ秒で完了
logicnp (ToCharArray) の方法は 296 ミリ秒で完了
oleksii (StringBuilder) の方法が 416 ミリ秒で完了
André Christoffer Andersen (Lambda w/ Aggregate) の方法は 870 ミリ秒で完了しました
Richard (MatchEvaluator を使用した正規表現) の方法は 1722 ミリ秒で完了しました
Marc Gravell (静的正規表現) の方法は 395 ミリ秒で完了しました
user1664043 (StringBuilder w/ IndexOfAny) の方法は 459 ミリ秒で完了しました
*** パス 2
古い (Chained String.Replace()) 方法は 215 ミリ秒で完了
logicnp (ToCharArray) の方法は 239 ミリ秒で完了
oleksii (StringBuilder) の方法が 341 ミリ秒で完了
André Christoffer Andersen (Lambda w/ Aggregate) の方法は 758 ミリ秒で完了しました
Richard (MatchEvaluator を使用した正規表現) の方法は 1591 ミリ秒で完了しました
Marc Gravell (静的正規表現) の方法は 354 ミリ秒で完了しました
user1664043 (StringBuilder w/ IndexOfAny) の方法は 426 ミリ秒で完了しました
*** パス 3
古い (Chained String.Replace()) 方法は 199 ミリ秒で完了
logicnp (ToCharArray) の方法は 265 ミリ秒で完了
oleksii (StringBuilder) の方法が 337 ミリ秒で完了
André Christoffer Andersen (Lambda w/ Aggregate) の方法は 817 ミリ秒で完了しました
Richard (MatchEvaluator を使用した正規表現) の方法は 1666 ミリ秒で完了しました
Marc Gravell (静的正規表現) の方法は 373 ミリ秒で完了しました
user1664043 (StringBuilder w/ IndexOfAny) の方法は 412 ミリ秒で完了しました
*** パス 4
古い (Chained String.Replace()) 方法は 199 ミリ秒で完了
logicnp (ToCharArray) の方法は 230 ミリ秒で完了
oleksii (StringBuilder) の方法が 324 ミリ秒で完了
André Christoffer Andersen (Lambda w/ Aggregate) の方法は 791 ミリ秒で完了しました
Richard (MatchEvaluator を使用した正規表現) の方法は 1699 ミリ秒で完了しました
Marc Gravell (静的正規表現) の方法は 359 ミリ秒で完了しました
user1664043 (StringBuilder w/ IndexOfAny) の方法は 413 ミリ秒で完了しました
そして更新されたコード:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace Test.StringReplace
{
class Program
{
static string test1 = "A^@[BCD";
static string test2 = "E]FGH\\";
static string test3 = "ijk`l}m";
static string test4 = "nopq~{r";
static readonly Dictionary<char, string> repl =
new Dictionary<char, string>
{
{'^', "Č"}, {'@', "Ž"}, {'[', "Š"}, {']', "Ć"}, {'`', "ž"}, {'}', "ć"}, {'~', "č"}, {'{', "š"}, {'\\', "Đ"}
};
static readonly Regex replaceRegex;
static readonly char[] badChars = new char[] { '^', '@', '[', ']', '`', '}', '~', '{', '\\' };
static readonly char[] replacementChars = new char[] { 'Č', 'Ž', 'Š', 'Ć', 'ž', 'ć', 'č', 'š', 'Đ' };
static Program() // static initializer
{
StringBuilder pattern = new StringBuilder().Append('[');
foreach (var key in repl.Keys)
pattern.Append(Regex.Escape(key.ToString()));
pattern.Append(']');
replaceRegex = new Regex(pattern.ToString(), RegexOptions.Compiled);
}
public static string Sanitize(string input)
{
return replaceRegex.Replace(input, match =>
{
return repl[match.Value[0]];
});
}
static string DoGeneralReplace(string input)
{
var sb = new StringBuilder(input);
return sb.Replace('^', 'Č').Replace('@', 'Ž').Replace('[', 'Š').Replace(']', 'Ć').Replace('`', 'ž').Replace('}', 'ć').Replace('~', 'č').Replace('{', 'š').Replace('\\', 'Đ').ToString();
}
//Method for replacing chars with a mapping
static string Replace(string input, IDictionary<char, char> replacementMap)
{
return replacementMap.Keys
.Aggregate(input, (current, oldChar)
=> current.Replace(oldChar, replacementMap[oldChar]));
}
static string ReplaceCharsWithIndexOfAny(string sIn)
{
int replChar = sIn.IndexOfAny(badChars);
if (replChar < 0)
return sIn;
// Don't even bother making a copy unless you know you have something to swap
StringBuilder sb = new StringBuilder(sIn, 0, replChar, sIn.Length + 10);
while (replChar >= 0 && replChar < sIn.Length)
{
var c = replacementChars[replChar];
sb.Append(c);
////// This approach lets you swap a char for a string or to remove some
////// If you had a straight char for char swap, you could just have your repl chars in an array with the same ordinals and do it all in 2 lines matching the ordinals.
////c = c switch
////{
//// ////case "^":
//// //// c = "Č";
//// //// ...
//// '\ufeff' => null,
//// _ => replacementChars[replChar],
////};
////if (c != null)
////{
//// sb.Append(c);
////}
replChar++; // skip over what we just replaced
if (replChar < sIn.Length)
{
int nextRepChar = sIn.IndexOfAny(badChars, replChar);
sb.Append(sIn, replChar, (nextRepChar > 0 ? nextRepChar : sIn.Length) - replChar);
replChar = nextRepChar;
}
}
return sb.ToString();
}
static void Main(string[] args)
{
for (int i = 1; i < 5; i++)
DoIt(i);
}
static void DoIt(int n)
{
Stopwatch sw = new Stopwatch();
int idx = 0;
Console.WriteLine("*** Pass " + n.ToString());
// old way
sw.Start();
for (idx = 0; idx < 500000; idx++)
{
string result1 = test1.Replace('^', 'Č').Replace('@', 'Ž').Replace('[', 'Š').Replace(']', 'Ć').Replace('`', 'ž').Replace('}', 'ć').Replace('~', 'č').Replace('{', 'š').Replace('\\', 'Đ');
string result2 = test2.Replace('^', 'Č').Replace('@', 'Ž').Replace('[', 'Š').Replace(']', 'Ć').Replace('`', 'ž').Replace('}', 'ć').Replace('~', 'č').Replace('{', 'š').Replace('\\', 'Đ');
string result3 = test3.Replace('^', 'Č').Replace('@', 'Ž').Replace('[', 'Š').Replace(']', 'Ć').Replace('`', 'ž').Replace('}', 'ć').Replace('~', 'č').Replace('{', 'š').Replace('\\', 'Đ');
string result4 = test4.Replace('^', 'Č').Replace('@', 'Ž').Replace('[', 'Š').Replace(']', 'Ć').Replace('`', 'ž').Replace('}', 'ć').Replace('~', 'č').Replace('{', 'š').Replace('\\', 'Đ');
}
sw.Stop();
Console.WriteLine("Old (Chained String.Replace()) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms");
Dictionary<char, char> replacements = new Dictionary<char, char>();
replacements.Add('^', 'Č');
replacements.Add('@', 'Ž');
replacements.Add('[', 'Š');
replacements.Add(']', 'Ć');
replacements.Add('`', 'ž');
replacements.Add('}', 'ć');
replacements.Add('~', 'č');
replacements.Add('{', 'š');
replacements.Add('\\', 'Đ');
// logicnp way
sw.Reset();
sw.Start();
for (idx = 0; idx < 500000; idx++)
{
char[] charArray1 = test1.ToCharArray();
for (int i = 0; i < charArray1.Length; i++)
{
char newChar;
if (replacements.TryGetValue(test1[i], out newChar))
charArray1[i] = newChar;
}
string result1 = new string(charArray1);
char[] charArray2 = test2.ToCharArray();
for (int i = 0; i < charArray2.Length; i++)
{
char newChar;
if (replacements.TryGetValue(test2[i], out newChar))
charArray2[i] = newChar;
}
string result2 = new string(charArray2);
char[] charArray3 = test3.ToCharArray();
for (int i = 0; i < charArray3.Length; i++)
{
char newChar;
if (replacements.TryGetValue(test3[i], out newChar))
charArray3[i] = newChar;
}
string result3 = new string(charArray3);
char[] charArray4 = test4.ToCharArray();
for (int i = 0; i < charArray4.Length; i++)
{
char newChar;
if (replacements.TryGetValue(test4[i], out newChar))
charArray4[i] = newChar;
}
string result4 = new string(charArray4);
}
sw.Stop();
Console.WriteLine("logicnp (ToCharArray) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms");
// oleksii way
sw.Reset();
sw.Start();
for (idx = 0; idx < 500000; idx++)
{
string result1 = DoGeneralReplace(test1);
string result2 = DoGeneralReplace(test2);
string result3 = DoGeneralReplace(test3);
string result4 = DoGeneralReplace(test4);
}
sw.Stop();
Console.WriteLine("oleksii (StringBuilder) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms");
// André Christoffer Andersen way
sw.Reset();
sw.Start();
for (idx = 0; idx < 500000; idx++)
{
string result1 = Replace(test1, replacements);
string result2 = Replace(test2, replacements);
string result3 = Replace(test3, replacements);
string result4 = Replace(test4, replacements);
}
sw.Stop();
Console.WriteLine("André Christoffer Andersen (Lambda w/ Aggregate) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms");
// Richard way
sw.Reset();
sw.Start();
Regex reg = new Regex(@"\^|@|\[|\]|`|\}|~|\{|\\");
MatchEvaluator eval = match =>
{
switch (match.Value)
{
case "^": return "Č";
case "@": return "Ž";
case "[": return "Š";
case "]": return "Ć";
case "`": return "ž";
case "}": return "ć";
case "~": return "č";
case "{": return "š";
case "\\": return "Đ";
default: throw new Exception("Unexpected match!");
}
};
for (idx = 0; idx < 500000; idx++)
{
string result1 = reg.Replace(test1, eval);
string result2 = reg.Replace(test2, eval);
string result3 = reg.Replace(test3, eval);
string result4 = reg.Replace(test4, eval);
}
sw.Stop();
Console.WriteLine("Richard (Regex w/ MatchEvaluator) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms");
// Marc Gravell way
sw.Reset();
sw.Start();
for (idx = 0; idx < 500000; idx++)
{
string result1 = Sanitize(test1);
string result2 = Sanitize(test2);
string result3 = Sanitize(test3);
string result4 = Sanitize(test4);
}
sw.Stop();
Console.WriteLine("Marc Gravell (Static Regex) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms");
// user1664043 way
sw.Reset();
sw.Start();
for (idx = 0; idx < 500000; idx++)
{
string result1 = ReplaceCharsWithIndexOfAny(test1);
string result2 = ReplaceCharsWithIndexOfAny(test2);
string result3 = ReplaceCharsWithIndexOfAny(test3);
string result4 = ReplaceCharsWithIndexOfAny(test4);
}
sw.Stop();
Console.WriteLine("user1664043 (StringBuilder w/ IndexOfAny) way completed in " + sw.ElapsedMilliseconds.ToString() + " ms\n");
}
}
}