ToString()。Trim()を呼び出さずにStringBuilder
の末尾から空白を削除し、新しいSB new StringBuilder(sb.ToString().Trim())
に戻す効率的な方法は何ですか。
以下は拡張メソッドなので、次のように呼び出すことができます。
_ sb.TrimEnd();
_
また、SBインスタンスを返し、他の呼び出し(sb.TrimEnd().AppendLine()
)をチェーンできるようにします。
_ public static StringBuilder TrimEnd(this StringBuilder sb)
{
if (sb == null || sb.Length == 0) return sb;
int i = sb.Length - 1;
for (; i >= 0; i--)
if (!char.IsWhiteSpace(sb[i]))
break;
if (i < sb.Length - 1)
sb.Length = i + 1;
return sb;
}
_
ノート:
1)NullまたはEmptyの場合、戻ります。
2)トリムが実際に必要ない場合は、非常に短い戻り時間を話していることになります。おそらく、最も高価な呼び出しは、char.IsWhiteSpaceへの単一の呼び出しです。したがって、これらのToString()。Trim()がSBルートに戻るのとは対照的に、不要なときにTrimEndを呼び出すのに実質的に費用はかかりません。
3)それ以外の場合、トリムが必要な場合に最も費用がかかるのは、char.IsWhiteSpaceへの複数の呼び出しです(最初の非空白文字の改行)。もちろん、ループは逆方向に反復します。すべてが空白である場合、SB.Lengthは0になります。
4)空白が見つかった場合、iインデックスはループの外側に保持されるため、Lengthを適切にカットできます。 StringBuilderでは、これは非常にパフォーマンスが高く、内部長整数を設定するだけです(内部char []は同じ内部長に保たれます)。
UPDATE2:以下のようにRyan Emerleによる優れたメモを参照してください。これにより、誤解の一部が修正されます(SBの内部動作は、私が作成したものよりも少し複雑です)になる):
StringBuilderは厳密にはchar []のブロックのリンクされたリストであるため、LOHになることはありません。別のチャンクに移動する場合、容量を維持する必要があるため、新しいチャンクを割り当てる必要がある場合があるため、長さを調整することは、技術的には終了インデックスを変更するほど簡単ではありません。それでも、最後に設定するのはLengthプロパティだけなので、これは素晴らしい解決策のようです。 Eric Lippertからの関連詳細: https://stackoverflow.com/a/6524401/62195
また、.NET 4.0の新しいStringBuilder実装について説明しているこの素晴らしい記事を参照してください。 http://1024strongoxen.blogspot.com/2010/02/net-40-stringbuilder-implementation.html
-------更新--------
以下は、StringBuilderの長さが変更されたときに何が起こるかを示しています(ここでSBに対して行われる唯一の実際の操作と、必要な場合のみ)。
_ StringBuilder sb = new StringBuilder("cool \t \r\n ");
sb.Capacity.Print(); // 16
sb.Length.Print(); // 11
sb.TrimEnd();
sb.Capacity.Print(); // 16
sb.Length.Print(); // 4
_
内部配列(m_ChunkChars)が長さを変更した後も同じサイズのままであることがわかります。実際、デバッガーでは(この場合は空白文字)も上書きされていないことがわかります。彼らは孤児ですすべてです。
あなたはこれを試すことができます:
StringBuilder b = new StringBuilder();
b.Append("some words");
b.Append(" to test ");
int count = 0;
for (int i = b.Length - 1; i >= 0; i--)
{
if (b[i] == ' ')
count++;
else
break;
}
b.Remove(b.Length - count, count);
string result = b.ToString();
空白がある間はループを抜けて最後まで反復します。
または次のようにも:
StringBuilder b = new StringBuilder();
b.Append("some words");
b.Append(" to test ");
do
{
if(char.IsWhiteSpace(b[b.Length - 1]))
{
b.Remove(b.Length - 1,1);
}
}
while(char.IsWhiteSpace(b[b.Length - 1]));
string get = b.ToString();
完全なトリムを行うには、StringBuilder
レベルで実行するのではなく、ToString
に実行することをお勧めします。このTrimToString
実装のように:
public static string TrimToString(this StringBuilder sb)
{
if (sb == null) return null;
sb.TrimEnd(); // handles nulle and is very inexpensive, unlike trimstart
if (sb.Length > 0 && char.IsWhiteSpace(sb[0])) {
for (int i = 0; i < sb.Length; i++)
if (!char.IsWhiteSpace(sb[i]))
return sb.ToString(i);
return ""; // shouldn't reach here, bec TrimEnd should have caught full whitespace strings, but ...
}
return sb.ToString();
}
StringBuilder myString = new StringBuilder("This is Trim test ");
if (myString[myString.Length - 1].ToString() == " ")
{
myString = myString.Remove(myString.Length - 1, 1);
}
public static class StringBuilderExtensions
{
public static StringBuilder Trim(this StringBuilder builder)
{
if (builder.Length == 0)
return builder;
var count = 0;
for (var i = 0; i < builder.Length; i++)
{
if (!char.IsWhiteSpace(builder[i]))
break;
count++;
}
if (count > 0)
{
builder.Remove(0, count);
count = 0;
}
for (var i = builder.Length - 1; i >= 0; i--)
{
if (!char.IsWhiteSpace(builder[i]))
break;
count++;
}
if (count > 0)
builder.Remove(builder.Length - count, count);
return builder;
}
}