C#で複数の文字列要素を置き換える


88

これを行うためのより良い方法はありますか...

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

正規表現オプションのスコアはひどいです。辞書オプションが最も速く表示されます。stringbuilder replaceの長巻きバージョンは、速記よりもわずかに高速です。


1
ベンチマークの内容に基づくと、辞書バージョンがすべての置換を行っているわけではないようです。これが、StringBuilderソリューションよりも高速になっていると思われます。
ヒキガエル

1
@toadこんにちは2009年から。私はその明白な間違いについて4月に以下にコメントを追加しました。Dをスキップしましたが、要点は更新されています。辞書バージョンの方が高速です。
クリスマッキー


1
@TotZamは、フラグを立てる前に少なくとも日付を確認してください。これは2009年から2012年からです
クリスマッキー

ここでの多くの回答はパフォーマンスに関係しているように思われるので、AndrejAdamankoの回答が多くの代替品の中で最も速い可能性が高いことを指摘しておく必要があります。彼の答えに述べられているように、特に大きな入力文字列では、.Replace()をチェーンするよりも確かに高速です。
person 2719

回答:


125

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

このメソッドStringsがかなりの長さの倍数でアクティブに呼び出されることが予想される場合は、その実装をStringBuilderクラスに「移行」する方がよい場合があります。これを使用すると、変更はそのインスタンスで直接実行されるため、不要なコピー操作を省くことができます。

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();
    }
}

2
明確にするために、辞書の答えは最速のstackoverflow.com/a/1321366/52912
Chris McKee

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

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

最高のパフォーマンスを得るには、文字をループして自分で置き換えます。ただし、複数の文字列がある場合は面倒な場合があります(それらを見つけると、一度に複数の文字を比較する必要がありますが、それらを置き換えるには、より多くのメモリを割り当て、文字列の残りを移動する必要があります)。
ChayimFriedman20年

14

あなたが単にかなりの解決策を求めていて、数ナノ秒を節約する必要がないのなら、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));

要旨の例Cと同様(その上を見ると、醜いlinqステートメントがコメントにあります)
Chris McKee

1
機能ステートメントを手続き型ステートメントよりも「醜い」と定義するのは興味深いことです。
TimS 2014

それについて議論するつもりはありません。その単なる好み。あなたが言うように、linqは単に構文糖衣です。そして、私が言ったように、私はすでにコードの上に同等のものを置いていました:)
Chris McKee

14

これはより効率的になります:

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();
    }
}

本当に読みにくい。あなたはそれが何をするのか知っていると確信していますが、ジュニア開発者は実際に何が起こっているのか頭を悩ませます。私も同意します-私はいつも何かを書くことのショートハンドを探します-しかしそれは私自身の満足のためだけでした。他の人々は混乱の山にびっくりしていました。
ピオトルクラ2013

3
これは実際には遅いです。BenchmarkOverhead ... 13ms StringClean-user151323 ... 2843ms StringClean-TheVillageIdiot ... 2921ms再実行によって異なりますが、答えは勝ちますgist.github.com/anonymous/5937596
Chris McKee

11

多分もう少し読みやすいですか?

    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に関するNewInTownの提案も追加してください...


5
次のように読みやすくなりますprivate static Dictionary<string, string> _replacements = new Dictionary<string, string>() { {"&", "and"}, {",", ""}, {" ", " "} /* etc */ };
。– ANevesは、SEは悪だと考えています2011

2
またはもちろん... private static readonly Dictionary <string、string> Replaces = new Dictionary <string、string>(){{"&"、 "and"}、{"、"、 ""}、{""、 ""} / *など* /}; public static string Clean(this string s){return Replaces.Keys.Aggregate(s、(current、toReplace)=> current.Replace(toReplace、Replacements [toReplace])); }
クリスマッキー2013

2
-1:辞書を使用しても、ここでは意味がありません。を使用するだけList<Tuple<string,string>>です。これはまた、置き換えが行われる順序を変更し、例えば、ほど速くはありませんs.Replace("a").Replace("b").Replace("c")。これは使わないでください!
トーマス

6

提案されたソリューションで最適化できることが1つあります。を何度も呼び出すとReplace()、同じ文字列に対して複数のパスを実行するコードが作成されます。文字列が非常に長い場合、CPUキャッシュ容量が失われるため、ソリューションが遅くなる可能性があります。1回のパスで複数の文字列を置き換えることを検討する必要があるかもしれません。


1
多くの回答がパフォーマンスに懸念を抱いているようですが、その場合はこれが最善です。また、String.Replaceのオーバーロード文書化されているだけなので、簡単です。この例では、辞書を使用して一致に基づいて期待値を返します。理解しやすいはずです。
person 2719

4

linqを使用する別のオプションは

[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);
}

宣言してvar removeList = new List<string> { /*...*/ };からremoveList.ForEach( /*...*/ );、コードを呼び出して単純化することができます。見つかったすべての文字列がString.Empty。に置き換えられるため、質問に完全には答えられないことにも注意してください。
Tok '

2

私は似たようなことをしていますが、私の場合はシリアル化/逆シリアル化を行っているので、両方向に進むことができる必要があります。string [] []の使用は、初期化を含め、辞書とほぼ同じように機能しますが、逆の方向に進んで、置換を元の値に戻すこともできます。これは、辞書では実際には設定されていません。

編集:Dictionary<Key,List<Values>>string [] []と同じ結果を得るために使用できます


-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]);
}

2
回答にコンテキストを追加することを検討する必要があります。それが何をしているのかについての簡単な説明のようにそして、もし関連があれば、なぜあなたはそれをあなたがしたように書いたのか。
ニール
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.