2009-08-24 17 views
56

これを行う良い方法がありますか?C#で複数の文字列要素を置き換える

MyString.Trim().Replace("&", "and").Replace(",", "").Replace(" ", " ") 
     .Replace(" ", "-").Replace("'", "").Replace("/", "").ToLower(); 

文字列クラスを拡張して1つのジョブにしましたが、より速い方法がありますか?

public static class StringExtension 
{ 
    public static string clean(this string s) 
    { 
     return s.Replace("&", "and").Replace(",", "").Replace(" ", " ") 
       .Replace(" ", "-").Replace("'", "").Replace(".", "") 
       .Replace("eacute;", "é").ToLower(); 
    } 
} 

ちょっと面白くて(そしてコメントの引数を止めるために) 私は下のさまざまな例をベンチマークして大事にしました。

https://gist.github.com/ChrisMcKee/5937656

正規表現オプションはひどくスコアを付けます。辞書オプションが最も速くなります。ストリングビルダの置き換えられた長いバージョンは、短い手よりもやや速いです。

+0

ですStringBuilderソリューション – toad

+1

@toadこんにちは2009年から。私はその驚くべき間違いについて4月に下記のコメントを追加しました。私はDをスキップしましたが、要点は更新されました。辞書のバージョンはまだまだ高速です。 –

+0

[代替のString.Replace何回かの重複の可能性がありますか?](http://stackoverflow.com/questions/12007358/alternative-to-string-replace-multiple-times) –

答えて

79

Quicker - no。より効果的 - はい、あなたがStringBuilderクラスを使用する場合。実装では、各操作によって文字列のコピーが生成され、状況によってはパフォーマンスが低下する可能性があります。文字列は不変オブジェクトなので、各操作は変更されたコピーを返します。

Stringsの長さがこのメソッドを積極的に呼び出すと予想される場合は、その実装をStringBuilderクラスに「移行する」方がよい場合があります。これにより、そのインスタンスに対して直接変更が行われるため、不要なコピー操作が不要になります。

public static class StringExtension 
{ 
    public static string clean(this string s) 
    { 
     return new StringBuilder(s) 
       .Replace("&", "and") 
       .Replace(",", "") 
       .Replace(" ", " ") 
       .Replace(" ", "-") 
       .Replace("'", "") 
       .Replace(".", "") 
       .Replace("eacute;", "é") 
       .ToString() 
       .ToLower(); 
    } 
} 
+1

分かりやすくするため、辞書の回答は最も速いhttp://stackoverflow.com/a/1321366/52912 –

+2

https://gist.github.com/ChrisMcKee/5937656のベンチマークでは、辞書テスト完全ではありません。すべての置換を行うのではなく、 ""ではなく ""を置き換えます。ベンチマークで最も速いのはなぜなのでしょうか?正規表現の置き換えも完了していません。しかし、最も重要なことに、あなたの文字列TestDataは非常に短いです。受け入れられた回答状態と同様に、StringBuilderが有効であるためには、文字列の長さがかなり長くなければなりません。 10kB、100kB、1MBの文字列でベンチマークを繰り返してください。 – Leif

+0

それは良い点です。それはURLクレンジングのために使用されていたので、100kb - 1mbでのテストは非現実的でした。私はベンチマークを更新するので、すべてを使用していますが、それは間違いでした。 –

8

これは、より効率的になりますか?

public static class StringExtension { 

     private static Dictionary<string, string> _replacements = new Dictionary<string, string>(); 

     static StringExtension() { 
      _replacements["&"] = "and"; 
      _replacements[","] = ""; 
      _replacements[" "] = " "; 
      // etc... 
     } 

     public static string clean(this string s) { 
      foreach (string to_replace in _replacements.Keys) { 
       s = s.Replace(to_replace, _replacements[to_replace]); 
      } 
      return s; 
     } 
    } 

またStringBuilderの程度タウンの提案では、新規を追加...

+0

本当に読みにくいです。私はあなたがそれが何をしているか知っていると確信していますが、ジュニア開発者は実際に何が起こっているかで頭を傷つけるでしょう。私は同意する - 私はいつも何かを書くことの短絡の手を探す - それは私の満足のためだけだった。他の人たちは混乱の嵐で驚いていた。 – ppumkin

+2

これは実際には遅いです。 BenchmarkOverhead ... 13msの StringClean-user151323 ... 2843ms StringClean-TheVillageIdiot ... 2921ms は再放送で異なるが、答えは https://gist.github.com/anonymous/5937596 –

10

たぶんもう少し読み:

public static class StringExtention 
{ 
    public static string clean(this string s) 
    { 
     StringBuilder sb = new StringBuilder (s); 

     sb.Replace("&", "and"); 
     sb.Replace(",", ""); 
     sb.Replace(" ", " "); 
     sb.Replace(" ", "-"); 
     sb.Replace("'", ""); 
     sb.Replace(".", ""); 
     sb.Replace("eacute;", "é"); 

     return sb.ToString().ToLower(); 
    } 
} 
+4

勝それはより多くのだろう'{""、 "and"}、{"、"、 "}、{" "、" "}このような読み込みが可能です:' private static Dictionary _replacements = new Dictionary }/* etc * /}; ' – ANeves

+1

またはもちろん... プライベート静的読み取り専用辞書置き換え=新しい辞書({{"& "、" and "}、{} "、"、 "}}、{" "、" "}/* etc * /}; \t \tパブリック静的文字列(この文字列s)クリーン \t \t { \t \t \t戻りReplacements.Keys.Aggregate(S、(現在、toReplace)=> current.Replace(toReplace、交換部品[toReplace]))。 \t \t} –

1

私は似た何かをやっているが、私の場合、私は行くことができるようにする必要があるので、シリアライズ/デシリアライズをやっています双方向。私は文字列[] []を使用して初期化を含む辞書とほぼ同じように動作することを知っていますが、あなたは元の値に代用品を返すこともできます。

編集:あなたは、文字列と同じ結果を得るためにDictionary<Key,List<Values>>を使用することができます[] []

4

あなたはかなり解決した後、単純であり、数ナノ秒を保存する必要がない場合は、どのようにいくつかのLINQ糖について?

var input = "test1test2test3"; 
var replacements = new Dictionary<string, string> { { "1", "*" }, { "2", "_" }, { "3", "&" } }; 

var output = replacements.Aggregate(input, (current, replacement) => current.Replace(replacement.Key, replacement.Value)); 
+0

Gistの例Cに似ています(上に見てみると、より冗長なlinq文がコメントにあります) –

+1

機能的な文章を手続き型のものより "醜い"と定義するのは面白いです。 – TimS

+0

それについて議論するつもりはない。単にそれの好みです。あなたが言うように、linqは単に構文的な砂糖です。私が言ったように、私はすでにコードの上に同等のものを置くだろう:) –

3

提案されている解決策では、最適化されるものが1つあります。 Replace()を多く呼び出すと、同じ文字列に対して複数のパスを実行するコードになります。非常に長い文字列を使用すると、CPUのキャッシュ容量ミスのためにソリューションが遅くなることがあります。考慮する必要がありますreplacing multiple strings in a single pass。LINQを使用して

-1
string input = "it's worth a lot of money, if you can find a buyer."; 
for (dynamic i = 0, repl = new string[,] { { "'", "''" }, { "money", "$" }, { "find", "locate" } }; i < repl.Length/2; i++) { 
    input = input.Replace(repl[i, 0], repl[i, 1]); 
} 
+1

答えにコンテキストを追加することを検討する必要があります。それが何をしているのかについての簡単な説明のように、そして関連性があるならば、なぜあなたがそれを書いたのか? – Neil

1

別のオプションあなたは辞書のバージョンがそれよりも速くなっている私がいる疑いがある代替品のすべてをやっていないように見えるあなたのベンチマークに持っているものに基づいて

[TestMethod] 
public void Test() 
{ 
    var input = "it's worth a lot of money, if you can find a buyer."; 
    var expected = "its worth a lot of money if you can find a buyer"; 
    var removeList = new string[] { ".", ",", "'" }; 
    var result = input; 

    removeList.ToList().ForEach(o => result = result.Replace(o, string.Empty)); 

    Assert.AreEqual(expected, result); 
} 
+0

'var removeList = new List {/*...*/};を宣言し、' removeList.ForEach(/*...*/); 'を呼び出してコードを単純化するだけです。また、* all *見つかった文字列は 'String.Empty'に置き換えられているため、完全には答えられません。 –

関連する問題