stringbuilderで構築された文字列から最後の文字を削除する最良の方法


90

私は以下を持っています

data.AppendFormat("{0},",dataToAppend);

これの問題は、それをループで使用していて、試行錯誤のカンマがあることです。末尾のコンマを削除する最良の方法は何ですか?

データを文字列、部分文字列に変更する必要がありますか?


10
string.Join(",", yourCollection)?編集:回答として追加されました。
Vlad

1
あなたはstackoverflow.com/questions/5701163/…を試しましたか?
andreister 2013年

@Chris:この方法では、StringBuilderはまったく必要ありません。
Vlad

後でカンマを削除する代わりに、カンマを追加しないようにすることができます。See:stackoverflow.com/questions/581448/…(Jon Skeet's answer)
Paolo Falabella

@Vladうん、ごめんなさい、私はそれを誤解しました。私はあなたがそれを完全に彼のループの代わりとしてではなく、最終的に構築された文字列を変更するための提案として提供していると思いました。(私はコメントをすぐに削除したと思ったが、そうではない!)
Chris Sinclair

回答:


222

最も簡単で最も効率的な方法は、次のコマンドを実行することです。

data.Length--;

これを行うことにより、ポインタ(つまり、最後のインデックス)を1文字戻しますが、オブジェクトの変更可能性は変更しません。実際、aのクリアStringBuilderLengthも同様にです(ただしClear()、実装がどのようなものであるかを明確にするために、実際にはメソッドを使用してください)。

data.Length = 0;

割り当てテーブルを変更しないためです。このようなバイトを認識したくないというようなことを考えてみてください。さて、を呼び出してもToString()、それ以降は何も認識しLengthません。これは、提供するものより多くのスペースを割り当てる可変オブジェクトであり、この方法で単純に構築されます。


2
re data.Length = 0;:それはまさにStringBuilder.Clearそれが行うことなのでStringBuilder.Clear、意図を明確にするために使用することが望ましいです。
ErenErsönmez2013年

@ErenErsönmez、十分に公正な友人、私はそれが何をClear()しているのかをもっと明確に述べるべきだったが、面白いことだ。これがメソッドの最初の行ですClear()。しかし、インターフェースが実際にを発行することを知っていましたかreturn this;。今、それは私を殺すものです。Length = 0すでに持っている参照の変更を設定し、なぜ自分自身を返すのですか?
Mike Perrenoud

12
「流暢に」使えるようになるためだと思います。Append自分自身も返します。
ErenErsönmez2013年

43

使うだけ

string.Join(",", yourCollection)

この方法StringBuilderでは、ループは必要ありません。




非同期のケースに関する長い追加。2019年の時点では、データが非同期で送信される場合のセットアップはまれではありません。

データが非同期コレクションにある場合、string.Joinオーバーロードの取得はありませんIAsyncEnumerable<T>。しかし、手動で作成して、次のコードをstring.Joinハッキングするのは簡単です

public static class StringEx
{
    public static async Task<string> JoinAsync<T>(string separator, IAsyncEnumerable<T> seq)
    {
        if (seq == null)
            throw new ArgumentNullException(nameof(seq));

        await using (var en = seq.GetAsyncEnumerator())
        {
            if (!await en.MoveNextAsync())
                return string.Empty;

            string firstString = en.Current?.ToString();

            if (!await en.MoveNextAsync())
                return firstString ?? string.Empty;

            // Null separator and values are handled by the StringBuilder
            var sb = new StringBuilder(256);
            sb.Append(firstString);

            do
            {
                var currentValue = en.Current;
                sb.Append(separator);
                if (currentValue != null)
                    sb.Append(currentValue);
            }
            while (await en.MoveNextAsync());
            return sb.ToString();
        }
    }
}

データが非同期で送られてくるが、インターフェースIAsyncEnumerable<T>がサポートされていない場合(コメントで述べたようにSqlDataReader)、データをにラップするのは比較的簡単IAsyncEnumerable<T>です:

async IAsyncEnumerable<(object first, object second, object product)> ExtractData(
        SqlDataReader reader)
{
    while (await reader.ReadAsync())
        yield return (reader[0], reader[1], reader[2]);
}

そしてそれを使う:

Task<string> Stringify(SqlDataReader reader) =>
    StringEx.JoinAsync(
        ", ",
        ExtractData(reader).Select(x => $"{x.first} * {x.second} = {x.product}"));

を使用するにはSelect、nugetパッケージを使用する必要がありますSystem.Interactive.Asyncここにコンパイル可能な例があります。


12
最良の答えは問題に対処するものではなく、それを防ぐものです。
LastTribunal 2014

1
@Seabizkit:もちろんです!ちなみに、質問全体はC#に関するものです。
Vlad

1
@Vlad私はそれを得ました、私が生のテストのような単純なテストをしていて、それらが同じものを生成しなかったので、ダブルチェックだけです。string.Join(",", yourCollection)まだ,終わりがあります。したがって、上記のIE string.Join(",", yourCollection)は非効率的であり、それ自体では削除されません。
Seabizkit 2017年

2
@Seabizkit:非常に奇妙です!例を投稿していただけませんか?私のコードでは完全に機能します:ideone.com/aj8PWR
Vlad

2
何年にもわたってループを使用して文字列ビルダーを構築し、その後に続くコンマを削除してきたので、私はこれをそのまま使用できました。先端をありがとう!
Caverman、2017

11

ループの後、次を使用します。

.TrimEnd(',')

または単にに変更

string commaSeparatedList = input.Aggregate((a, x) => a + ", " + x)

4
彼は文字列ではなくStringBuilderを使用しています。さらに、それは非常に効果的ではありません。最初に文字列に変換してからトリミングします。
Piotr Stapp

またはstring.Join(",", input)
Tvde1 2019

10

これはどう..

string str = "The quick brown fox jumps over the lazy dog,";
StringBuilder sb = new StringBuilder(str);
sb.Remove(str.Length - 1, 1);

7

私は文字列ビルダーの長さを操作することを好みます:

data.Length = data.Length - 1;

4
なぜ単純にしないのですdata.Length----data.Length
コーディング終了

私は通常、data.Length--を使用しますが、削除したい文字の後ろに空白値があるため、2文字前に移動する必要がありました。その場合、トリムも機能しなかったため、data.Length = data.Length-2; 働いた。
Caverman

Trimは文字列の新しいインスタンスを返しますが、stringbuilderオブジェクトの内容は変更しません
bastos.sergio

@GoneCoding Visual Basic .NETがサポートしていない--か、++ あなたは使用することができるdata.Length -= 1けれども、またはこの答えはあまりにも動作します。
Jason S

3

ループアルゴリズムを変更することをお勧めします。

  • 項目の後ではなく前にコンマを追加します
  • falseで始まるブール変数を使用して、最初のコンマを抑制します
  • テスト後にこのブール変数をtrueに設定します

2
これはおそらく、すべての提案の中で最も効率が悪い(そしてより多くのコードが必要)
コーディング終了

1
@Vladの回答をご覧ください
Noctis

3

string.Joinメソッドを使用して、項目のコレクションをカンマ区切りの文字列に変換する必要があります。これにより、先頭または末尾にコンマがなくなり、文字列が効率的に構築されます(不要な中間文字列なしで)。


2

はい、ループが完了したら、文字列に変換します。

String str = data.ToString().TrimEnd(',');

3
最初は文字列に変換してからトリミングします。
Piotr Stapp

2
@Garathあなたが「非効率的」を意味するなら、私は反対しないでしょう。しかし、それ効果的です。
DonBoitnott 2013年

2

2つのオプションがあります。最初のものは非常に簡単な使用Remove方法で、非常に効果的です。2番目の方法はToString、開始インデックスと終了インデックスで使用することです(MSDNドキュメント



1

最も簡単な方法は、Join()メソッドを使用することです。

public static void Trail()
{
    var list = new List<string> { "lala", "lulu", "lele" };
    var data = string.Join(",", list);
}

StringBuilderが本当に必要な場合は、ループの後に終了コンマをトリミングします。

data.ToString().TrimEnd(',');

4
data.ToString().TrimEnd(',');非効率的
bastos.sergio 2013年

1
また、あなたが「」それはで終わる複数行を有することができるようStringにStringBuilderオブジェクトを変換したくない場合があります
Fandango68
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.