TL; DR
Jeśli celem jest, aby dostać kawałek lub całość TREŚCI StringBuilder
„s w String
obiektu, należy użyć jego ToString
funkcję.Ale jeśli nie jesteś jeszcze gotowy do tworzenia łańcucha, lepiej traktować StringBuilder
jako tablicę znaków i działać w ten sposób, niż tworzyć kilka ciągów, których nie potrzebujesz.
Operacje na łańcuchach znaków w tablicy znaków mogą być skomplikowane przez lokalizację lub kodowanie, ponieważ łańcuch może być zakodowany na wiele sposobów (na przykład UTF8 lub Unicode), ale jego znaki (System.Char
) mają być 16-bitowym UTF16 wartości.
Napisałem następującą metodę, która zwraca indeks ciągu, jeśli istnieje w ramach StringBuilder
i -1 w przeciwnym razie. Za jego pomocą można utworzyć inne typowe metody String
, takie jak Contains
, i EndsWith
. Ta metoda jest lepsza od innych, ponieważ powinna odpowiednio obsługiwać lokalizację i obudowę i nie zmuszać użytkownika do dzwonienia pod numer ToString
pod numerem StringBuilder
. Tworzy jedną wartość śmieci, jeśli określisz, że wielkość liter powinna zostać zignorowana, i możesz to naprawić, aby zmaksymalizować oszczędność pamięci za pomocą funkcji Char.ToLower zamiast wstępnego obliczania małych liter ciągu, tak jak to robię w poniższej funkcji. EDYCJA: Ponadto, jeśli pracujesz z ciągiem zakodowanym w UTF32, będziesz musiał porównać dwa znaki naraz zamiast jednego.
Prawdopodobnie lepiej używać ToString
, chyba że zamierzasz zapętlać, pracować z dużymi ciągami i manipulować lub formatować.
public static int IndexOf(this StringBuilder stringBuilder, string str, int startIndex = 0, int? count = null, CultureInfo culture = null, bool ignoreCase = false)
{
if (stringBuilder == null)
throw new ArgumentNullException("stringBuilder");
// No string to find.
if (str == null)
throw new ArgumentNullException("str");
if (str.Length == 0)
return -1;
// Make sure the start index is valid.
if (startIndex < 0 && startIndex < stringBuilder.Length)
throw new ArgumentOutOfRangeException("startIndex", startIndex, "The index must refer to a character within the string.");
// Now that we've validated the parameters, let's figure out how many characters there are to search.
var maxPositions = stringBuilder.Length - str.Length - startIndex;
if (maxPositions <= 0) return -1;
// If a count argument was supplied, make sure it's within range.
if (count.HasValue && (count <= 0 || count > maxPositions))
throw new ArgumentOutOfRangeException("count");
// Ensure that "count" has a value.
maxPositions = count ?? maxPositions;
if (count <= 0) return -1;
// If no culture is specified, use the current culture. This is how the string functions behave but
// in the case that we're working with a StringBuilder, we probably should default to Ordinal.
culture = culture ?? CultureInfo.CurrentCulture;
// If we're ignoring case, we need all the characters to be in culture-specific
// lower case for when we compare to the StringBuilder.
if (ignoreCase) str = str.ToLower(culture);
// Where the actual work gets done. Iterate through the string one character at a time.
for (int y = 0, x = startIndex, endIndex = startIndex + maxPositions; x <= endIndex; x++, y = 0)
{
// y is set to 0 at the beginning of the loop, and it is increased when we match the characters
// with the string we're searching for.
while (y < str.Length && str[y] == (ignoreCase ? Char.ToLower(str[x + y]) : str[x + y]))
y++;
// The while loop will stop early if the characters don't match. If it didn't stop
// early, that means we found a match, so we return the index of where we found the
// match.
if (y == str.Length)
return x;
}
// No matches.
return -1;
}
Głównym powodem jeden powszechnie używa StringBuilder
obiekt zamiast łączenie ciągów jest ze względu na pamięć napowietrznych ponieść ponieważ ciągi są niezmienne. Wydajność, którą widzisz, gdy wykonujesz nadmierne manipulacje strunami bez używania StringBuilder
jest często wynikiem zbierania wszystkich ciągów śmieci, które utworzyłeś po drodze.
Weź to na przykład:
string firstString = "1st",
secondString = "2nd",
thirdString = "3rd",
fourthString = "4th";
string all = firstString;
all += " & " + secondString;
all += " &" + thirdString;
all += "& " + fourthString + ".";
Jeśli było uruchomić to i otwórz go w profilera pamięci, można znaleźć zbiór łańcuchów, które wyglądają mniej więcej tak:
"1st", "2nd", "3rd", "4th",
" & ", " & 2nd", "1st & 2nd"
" &", "&3rd", "1st & 2nd &3rd"
"& ", "& 4th", "& 4th."
"1st & 2nd &3rd& 4th."
To czternaście obiektów, które stworzyliśmy w tym zakresie, ale jeśli nie zdajesz sobie sprawy, że każdy operator dodający tworzy cały nowy ciąg za każdym razem, gdy myślisz, że jest ich tylko pięć. Co dzieje się z dziewięcioma innymi strunami? Odkładają się w pamięci, dopóki śmieciarz nie zdecyduje się ich odebrać.
Więc teraz do rzeczy: jeśli próbujesz znaleźć coś o obiekcie StringBuilder
i nie chcesz dzwonić pod numer ToString()
, prawdopodobnie oznacza to, że nie skończyłeś jeszcze budować tego łańcucha. A jeśli próbujesz dowiedzieć się, czy budowniczy kończy się na "Foo", marnowanie czasu na wywoływanie sb.ToString(sb.Length - 1, 3) == "Foo"
, ponieważ tworzysz kolejny obiekt typu string, który zostaje osierocony i przestarzały w momencie, w którym wykonałeś połączenie.
Zgaduję, że uruchamiasz pętlę agregującą tekst do swojej StringBuilder
i chcesz zakończyć pętlę lub po prostu zrobić coś innego, jeśli kilka ostatnich znaków jest wartością wartą wartownika, którego się spodziewasz.
Odwróć ciąg znaków i zrób 'StartsWith (" world ")' maybe? – PoweredByOrange
Dlaczego boisz się 'ToString'? W ten sposób korzystasz z StringBuilders – banging
@PoweredByOrange Hm ... StringBuilder nie ma metody StartsWith i odwrotnie, będzie to bardziej kosztowne pod względem wydajności, po prostu zacznij sprawdzać od końca, char char. –