R.Tanaka.Ichiro's Blog

主にC# な話題です

目次

Blog 利用状況

ニュース

文字列置換

文字列置換
http://blogs.wankuma.com/mura/archive/2008/01/10/116868.aspx
(むらぶろ - .NETって面白い - )

のコメントを書いていたら長くなったので、自分のところで話題にしてみました。

>あくまでも想像ですが、String.Replaceでは、文字列を置換しつつ新しいStringオブジェクトを生成するのに対し、StringBuilderでは、①置>換対象の捜索、②置換文字列以降の位置をずらす、③置換文字の挿入、等の処理を実行している為だと思います。

僕も同意見です。
そこで以下のコードで実験してみました。

コードを修正しました


static void Main() {
  var a = (from p in Enumerable.Range(0, 10000000) select (p % 10).ToString()).ToArray();
  var source = String.Join(String.Empty, a);

  for(var i = 1; i <= 10; ++i) {
    Console.WriteLine(i.ToString() + "回目");
    Console.WriteLine("--------------------");
    Console.WriteLine("置換前文字数 < 置換後文字数 の比較");
    TestReplace(source, "0", "abcde");

    Console.WriteLine("--------------------");
    Console.WriteLine("置換前文字数 > 置換後文字数 の比較");
    TestReplace(source, "0123", "a");

    Console.WriteLine("--------------------");
    Console.WriteLine("置換前文字数 = 置換後文字数 の比較");
    TestReplace(source, "0", "a");    
    Console.WriteLine("--------------------");
  }
}
static void TestReplace(string source, string target, string replace) {
  var b = new StringBuilder(source);
  TestReplaceFunction(source, "String:", delegate { source.Replace(target, replace); });
  TestReplaceFunction(source, "StringBuilder:", delegate { b.Replace(target, replace); });
}
static void TestReplaceFunction(string source, string title, Action action) {
  var w = new Stopwatch();
  w.Start();
  action();
  w.Stop();
  Console.WriteLine(title + w.ElapsedMilliseconds);
}

1回目
--------------------
置換前文字数 < 置換後文字数 の比較
String:199
StringBuilder:193
--------------------
置換前文字数 > 置換後文字数 の比較
String:216
StringBuilder:287
--------------------
置換前文字数 = 置換後文字数 の比較
String:467
StringBuilder:166
--------------------
2回目
--------------------
置換前文字数 < 置換後文字数 の比較
String:212
StringBuilder:179
--------------------
置換前文字数 > 置換後文字数 の比較
String:174
StringBuilder:162
--------------------
置換前文字数 = 置換後文字数 の比較
String:189
StringBuilder:169
--------------------
3回目
--------------------
置換前文字数 < 置換後文字数 の比較
String:221
StringBuilder:174
--------------------
置換前文字数 > 置換後文字数 の比較
String:171
StringBuilder:148
--------------------
置換前文字数 = 置換後文字数 の比較
String:234
StringBuilder:197
--------------------
4回目
--------------------
置換前文字数 < 置換後文字数 の比較
String:252
StringBuilder:242
--------------------
置換前文字数 > 置換後文字数 の比較
String:689
StringBuilder:420
--------------------
置換前文字数 = 置換後文字数 の比較
String:346
StringBuilder:162
--------------------
5回目
--------------------
置換前文字数 < 置換後文字数 の比較
String:211
StringBuilder:245
--------------------
置換前文字数 > 置換後文字数 の比較
String:180
StringBuilder:151
--------------------
置換前文字数 = 置換後文字数 の比較
String:299
StringBuilder:369
--------------------
6回目
--------------------
置換前文字数 < 置換後文字数 の比較
String:259
StringBuilder:172
--------------------
置換前文字数 > 置換後文字数 の比較
String:198
StringBuilder:153
--------------------
置換前文字数 = 置換後文字数 の比較
String:298
StringBuilder:382
--------------------
7回目
--------------------
置換前文字数 < 置換後文字数 の比較
String:244
StringBuilder:179
--------------------
置換前文字数 > 置換後文字数 の比較
String:165
StringBuilder:154
--------------------
置換前文字数 = 置換後文字数 の比較
String:196
StringBuilder:171
--------------------
8回目
--------------------
置換前文字数 < 置換後文字数 の比較
String:428
StringBuilder:265
--------------------
置換前文字数 > 置換後文字数 の比較
String:169
StringBuilder:157
--------------------
置換前文字数 = 置換後文字数 の比較
String:287
StringBuilder:367
--------------------
9回目
--------------------
置換前文字数 < 置換後文字数 の比較
String:240
StringBuilder:165
--------------------
置換前文字数 > 置換後文字数 の比較
String:177
StringBuilder:153
--------------------
置換前文字数 = 置換後文字数 の比較
String:273
StringBuilder:168
--------------------
10回目
--------------------
置換前文字数 < 置換後文字数 の比較
String:201
StringBuilder:179
--------------------
置換前文字数 > 置換後文字数 の比較
String:169
StringBuilder:151
--------------------
置換前文字数 = 置換後文字数 の比較
String:330
StringBuilder:306
--------------------


結果的に「どっちもあまり変わらない」ってことみたいです。

うーん、もう少し違う答えがでるかと思ったんだけどなぁ。

投稿日時 : 2008年1月11日 11:04

Feedback

# re: 文字列置換 2008/01/11 11:17 囚人

むらさんとこでコメントしましたが、実験の仕方が少々不公平ですね。

# re: 文字列置換 2008/01/11 11:25 melt

String は immutable なクラスです。コンストラクト時に文字列が決まれば、それ以降絶対に変更することはありません。
これによって String の各メソッドはスレッドセーフになることが保証されています。
デザインパターンの一つである Immutable パターンについて調べてみればいいかと。
対して StringBuilder は mutable なクラスです。各メソッドはスレッドセーフではないです。

# re: 文字列置換 2008/01/11 12:13 なちゃ

置換内容が変わってしまっては比較の意味が…
こういうのって、慎重にやらないと痛い目にあいますよ。
間違ったやり方での結果を信じてそれを根拠にしてしまうと、どういう結果になるか…

# re: 文字列置換 2008/01/11 12:48 むら

皆様のご指摘の通り、私の所の実験ではStringとStringBuilderの置換内容が変わってしまっています。
変換結果をConsole.Writeして検証頂ければと思います。

ちなみに訂正したプログラムで実行した所、StringBuilderの方がパフォーマンスは良かったです。

> なちゃさん
> こういうのって、慎重にやらないと痛い目にあいますよ。

以前もなちゃさんには教えて頂きました。
その節は有難うございました。

「慎重」ですね...自分に足りない点と深く反省します。
恥を晒した訳ではありますが、皆様にご指摘を頂いて勘違いを思い知ることが出来、大きな収穫を得ました。
Rさん、皆様、お騒がせして申し訳御座いませんでした。

# re: 文字列置換 2008/01/11 13:17 よねけん

>文字列の挿入処理が行われない場面では、StringBuilder の置換の方が速いくらいです。

未検証ですが、挿入処理が行われる場合は、

var builder = new StringBuilder(source.Length*2); // 適当にソース文字列の2倍分とってるけど理想は置換後の文字列の長さ
builder .Append(source)

のように置換後の予想サイズをコンストラクタで指定すると
処理時間がたぶん違ってくると思います。

//必要であれば処理後に
builder.Capacity = builder.Length

# re: 文字列置換 2008/01/11 13:38 R・田中一郎

囚人 さん

>むらさんとこでコメントしましたが、実験の仕方が少々不公平ですね。

ああ、言われてみれば確かにそうですね。
コードを参考にしながら、僕も「ん?」と思ったのですが、思った通りの結果がでたので、あまり気にしませんでしたw

---------------------------------------------------------
melt さん

なるほど、わかりやすいご説明をありがとうございました。

---------------------------------------------------------
なちゃ さん

>置換内容が変わってしまっては比較の意味が…

僕の検証したかったのは、String.Replace と StringBuilder.Replace の速度についてなので、検証コードとしては適当ではないですね。

---------------------------------------------------------
むら さん

>ちなみに訂正したプログラムで実行した所、StringBuilderの方がパフォーマンスは良かったです。

僕の結果では「両者ともあまり変わらない」となりました。

# re: 文字列置換 2008/01/11 13:38 R・田中一郎

間違った結果をあげておくのもアレなので、修正しました。

# re: 文字列置換 2008/01/11 13:41 R・田中一郎

よねけん さん

そうか、Capacity によっても結果は異なるんですねぇ。

# re: 文字列置換 2008/01/11 14:57 なちゃ

これだと、
String.Replaceの速度と、
StringBuilderの初期化とReplaceとToStringの速度
の比較になっています。

まあこれだとStringの方が速い可能性が高いでしょう(もちろん実装によりますが)。

# re: 文字列置換 2008/01/11 16:21 R・田中一郎

純粋に replace だけの比較にしてみました。
でも結果が不定なんですよね・・・

ひょっとして、

StringBuilder.Replace()って
StringBuilder.ToString().Replace と同じ実装かな?

# re: 文字列置換 2008/01/11 16:26 なちゃ

StringBuilderは結構複雑な動きだった気がしますね。
ToStringではバッファはコピーされず、その後変更しようとすると新しいバッファが確保されるとか読んだ気がします。

まあ不定なばあいはそんなもんだと思っといたほうが精神衛生上はよろしいと思います。
※元々あまり気にする必要のない部分の比較ですし。

# re: 文字列置換 2008/01/11 16:35 R・田中一郎

そうみたいですね orz

>ToStringではバッファはコピーされず、その後変更しようとすると新しいバッファが確保されるとか読んだ気がします。

面白いですね。
でかいデータを扱う可能性があるから、それなりの細工はしているだろうことは想像していたのですが。

# stringbuilder+replace-でつながるブログリング 2009/01/14 8:39 blogring.org

stringbuilder+replaceに関するブログをまとめています。

# KMNpbrEtwyMi 2011/12/22 19:51 http://www.discreetpharmacist.com/

kq6rYY I must admit, the webmaster is a cool guy..!

# nCoMwDYlyxbwEQyM 2011/12/23 0:15 http://247options.com/

The topic is pretty complicated for a beginner!...

# wKbOHCyOhiaIQee 2011/12/23 18:29 http://247options.com/

Comrade kill yourself.

タイトル  
名前  
Url
コメント