Что такое эффективный способ обрезать пробелы с конца StringBuilder
без вызова ToString(). Trim() и обратно к новому SB new StringBuilder(sb.ToString().Trim())
.
Обрезать пробелы из конца StringBuilder, не вызывая ToString(). Trim() и обратно к новому SB
Ответ 1
Ниже приведен метод расширения, поэтому его можно вызвать так:
sb.TrimEnd();
Кроме того, он возвращает экземпляр SB, позволяя вам sb.TrimEnd().AppendLine()
другие вызовы (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) Если ноль или пусто, возвращает.
2) Если на самом деле нет необходимости в Trim, мы говорим об очень быстром времени возврата, при этом, вероятно, самым дорогим вызовом является одиночный вызов char.IsWhiteSpace. Таким образом, практически нет затрат на вызов TrimEnd, когда он не нужен, в отличие от этих ToString(). Trim() обратно на маршруты SB.
3) Иначе, самой дорогой вещью, если необходима подстройка, являются множественные вызовы char.IsWhiteSpace (разрывы на первом непробельном символе). Конечно, цикл повторяется в обратном направлении; если все пробелы, вы получите SB.Length 0.
4) Если были обнаружены пробелы, индекс я сохраняется вне цикла, что позволяет нам соответствующим образом сократить длину. В StringBuilder это невероятно эффективно, оно просто устанавливает целое число внутренней длины (внутренний символ [] остается той же внутренней длины).
ОБНОВЛЕНИЕ 2: посмотрите отличные заметки Райана Эмерла следующим образом, которые исправляют некоторые из моих недоразумений (внутренняя работа SB немного сложнее, чем я предполагал):
StringBuilder - это технически связанный список блоков char [], поэтому мы не попадаем в LOH. Регулировка длины технически не так проста, как изменение конечного индекса, потому что если вы переходите в другой блок, необходимо сохранить Capacity, поэтому может потребоваться выделить новый блок. Тем не менее, вы устанавливаете только свойство Length в конце, так что это кажется отличным решением. Соответствующие подробности от Эрика Липперта: fooobar.com/questions/163664/...
Также см. Эту хорошую статью, в которой обсуждается новая реализация StringBuilder.NET 4.0: 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) остается неизменным по размеру после изменения длины, и фактически вы можете видеть в отладчике, что он даже не перезаписывает (в данном случае пробельные символы). Они осиротели, это все.
Ответ 2
Вы можете попробовать следующее:
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();
Ответ 3
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;
}
}
Ответ 4
Для полной обрезки делать это не целесообразно/не рекомендуется на уровне 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();
}
Ответ 5
StringBuilder myString = new StringBuilder("This is Trim test ");
if (myString[myString.Length - 1].ToString() == " ")
{
myString = myString.Remove(myString.Length - 1, 1);
}