String nie jest przystosowany do typowych operacji na napisach.
W rzeczywistość programista chce tworzyć nowy łańcuch znaków , dodając do niego poszczególne fragmenty w kolejnych operacjach pętli. Pozwala na to StringBuilder.
StringBuilder
Klasa System.Text.StringBuildersłuży do tworzenia i modyfikowania łańcuchów znaków. Z czego składa się ta klasa.
Metoda | Opis działania |
Append() | Przeciążona metoda publiczna, która dodaje łańcuch znaków na koniec aktualnego obiektu. |
AppendFormat() | Przeciążona metoda publiczna, która zamienia specyfikator formatu aktualnego łańcucha na sformatowaną wartość obiektu. W skrócie działa tak samo, jak String.Format() |
AppendLine() | Dodaje łańcuch do jednej linii. |
Clear() | Czyści obiekt. |
Insert() | Przeciążona metoda publiczna, która wstawia łańcuch znaków na określoną pozycję. |
Remove() | Usuwa określone znaki. |
Replace() | Przeciążona metoda publiczna, która zamienia wszystkie wystąpienia danych znaków na nowe znaki. |
EnsureCapacity() | Sprawdzenie czy obiekt StrinBulider może pomieść określoną liczbę znaków. |
W przeciwieństwie do obiektu typu String. obiekt typu StringBuilder podlega zamianom. Kiedy program modyfikuje wartość obiektu StringBuilder, nie tworzy kopii, ale zmienia aktualny łańcuch znaków.
StringBuilder Append() Przykład użycia
Oto najprostszy przykład użycia StringBuilder. Za pomocą metody “AppednLine()” dodaje kolejne linijki tekstu ,a potem na końcu zawartość obiektu zmieniam na string za pomocą metody “ToString()”. Jego zawartość wynosi "Linia 1:\r\nLinia 2:\r\n"
StringBuilder sb = new StringBuilder();
sb.AppendLine("Linia 1:");
sb.AppendLine("Linia 2:");
string calyNapis = sb.ToString();
W przypadku użycia typu string do tego samego zadania z każdą kolejną operacją dodania teksu tworzysz nowy obiekt. Garbage Collector musi posprzątać parę obiektów pod koniec takiej metody lub w innym momencie.
string s = string.Empty;
s += "Linia 1:"; //stworzenie nowego obiektu
s += "/r/nLinia 2:/r/n"; //stworzenie nowego obiektu
StringBuilder.AppendFormat działa podobnie jak String.Format, który został opisany tutaj.
int a = 12;
double b = 0.113;
float c = -1.2f;
sb.AppendFormat("{0}:_{1}_ , *{2}* ",a,b,c);
//{12:_0,113_ , *-1,2* }
StringBuilder Metoda Insert()
Działanie tej metody jest proste wystarczy tylko podać odpowiedni indeks i do tego miejsca zostanie dodany nowy obiekt. Gorzej z określeniem tego indeksu.
Oczywiście indeks nie może być przekroczony.
StringBuilder Metoda Remove()
Metoda ta usuwa określoną liczbę znaków od podanego indeksu.
Prosty przykład:
StringBuilder sb = new StringBuilder();
sb.Append("MotoMyszy z Marsa");
sb.Remove(0,9);// z Marsa
StringBuilder Metoda Replace()
Tutaj jest co opisywać, ponieważ metoda ta ma kilka wersji. Metoda Replace() zastępuje znak bądź napis innym znakiem, bądź napisem. Możemy wybrać, od którego indeksu cały proces ma się rozpocząć.
W komentarzach opisałem, z której wersji przeciążonej korzystam i co ona zwraca.
StringBuilder sb = new StringBuilder();
sb.Append("_Zmiana__Znaków_");
//1: char old, char new
sb.Replace('_', ' ');
//{ Zmiana Znaków }
StringBuilder sb2 = new StringBuilder("An Johnsons...");
//2: string old, string new
sb2.Replace("An","The");
//{The Johnsons...}
StringBuilder sb3 = new StringBuilder();
sb3.Append("_Zmiana__Znaków_");
//3: char old, char new , int starIindex, int count
sb3.Replace('Z','z',7,3);
//{_Zmiana__znaków_}
//4: string old, string new, int startIndex, int count
StringBuilder sb4 = new StringBuilder("an johnsons have an owl");
sb4.Replace("an", "The", 0, 3);
//{The johnsons have an owl}
Przejdźmy do właściwości.
Właściwości StringBuilder
Klasa StringBuilderposiada parę właściwości.
Metoda | Opis działania |
Length | Pobiera bądź przypisuje liczbę znaków StringBulider |
MaxCapacity | Pokazuje maksymalną liczbę znaków, które mogą być przechowywane przez ten obiekt. |
Capacity | Pokazuje bądź przypisuje ile znaków może przechowywać obecna komórka pamięci, gdzie znajduje się StringBulider. |
Chars | Mechanizm indeksowania. |
Trochę się zdziwiłem ,że można zmienić liczbę znaków StringBulider co oczywiście ma wpływ na jego zawartość.
Z ciekawości nawet coś sprawdziłem. Spróbowałem usunąć zawartość, która normalnie byłaby poza indeksem.
StringBuilder sb = new StringBuilder();
sb.Append("12345");
sb.Length = 100;
sb.Remove(90, 9);//nie ma wyjątkustring ss = sb.ToString();
Oczywiście napis otrzymał puste znaki w wyniku zwiększenia jego długości.
W takim wypadku użyłbym metody Trim() do usunięcia białych znaków, zwłaszcza że są na końcu. Opisałem tą metodę wcześniej.
MaxCapacity określa ile znaków może pomieścić ten obiekt.
(2.147.483.647) Dwa miliardy znaków to całkiem dużo (zwłaszcza, zwłaszcza że w tle robię kopię zapasową Windows 7) . Zakładam ,że ta wartość jest zależna od komputera. Po przekroczeniu tego poziomu zapewne wyskakuje wyjątek “OutOfMemory”.
Co do właściwości Capacity pytanie powinno brzmieć co się dzieje, gdy ta wartość zostanie przekroczona. Wartość ta nie powinna nigdy być ustawiona na mniejszą niż jego długość napisu.
Z logicznego punktu widzenia ,aby komputer nie zwiększał wartości Capacity co chwilę przy skomplikowanych operacjach w StringBuilder. ,warto ustawić tę wartość z góry na dużą. Zwłaszcza że zwiększenie tej wartość w żaden sposób nie oddziałuje na napis zawarty w StringBuilder.
Wielkość Capacity może być ustawiona z góry w konstruktorze StringBuilder. Domyślnie jest to 16 znaków, ja tutaj ustawiłem 128.
StringBuilder sb = new StringBuilder(128);
StringBulider obsługuje indeksory char więc do każdego znaku w tym obiekcie można uzyskać dostęp jakby to była tablica.
StringBuilder sb = new StringBuilder(128);
sb.Append("12345");
char znak = sb[2];
Podsumowanie
Podsumowując StringBuliderzostał głównie stworzony z myślą o przechowywaniu dużej ilości tekstu, do której trzeba dodać kolejny duży tekst. StringBulider jednak nie oferuje tylu metod co zwykły string. StringBuilder nie posiada też metod rozszerzeniowych LINQ.
Czy warto go używać? Nawet jeśli często to wymaga to trochę kodu. Przykładowo, zamiast używać metody String.Join (która łączy napisy z kolekcji) można użyć własnej pętli i StringBuilder.
Eee nie.
Akurat jest to zły przykład, ponieważ metoda String.Join wykonuje dużo więcej rzeczy niż dodanie napisów w pętli foreach. Jest to jednak dobry przykład ,że prosta logika“StringBulider nie tworzy nowych obiektów, co znaczy ,że działa lepiej” - nie zawsze może być odniesiona do wszystkiego.
Oczywiście są przypadki, w których StringBulider ma całkowity sens. Głównie w przypadkach, gdy trzeba dodać napis w określony sposób.
int[] tabInt = new int[]{323,459,393,423,539,672,762,852,911,1432};
StringBuilder sb = new StringBuilder(128);
for (int i = 0; i < tabInt.Length; i++)
{
sb.AppendLine("*********************************");
sb.Append(i + 1);
sb.Append(":::");
sb.AppendLine();
sb.AppendFormat("\t {0} liczba z tablicy",tabInt[i]);
sb.AppendLine("\n\r");
}
Console.Write(sb.ToString());
Koniec
Na tym chyba skończę dział “C# i NET 4.0 operacje na napisach”. Wyrażenia regularne wymagają oddzielnego działu.