13. Łańcuchy w C# 2.0

13. Łańcuchy w C# 2.0

Autor: Paweł Kruczkowski

Opublikowano: 12/19/2006, 12:00 AM

Liczba odsłon: 89111

Podobnie jak w większości innych języków programowania, łańcuch (ang. string) to sekwencja znaków. W języku C# 2.0 łańcuch ten jest pełnoprawnym, efektywnym i co najważniejsze łatwym w użyciu typem. Niniejszy artykuł przybliży nam sposób używania typu string oraz klasy String, a także zdefiniuje podłańcuchy i sposoby ich łączenia. Nauczymy się również tworzyć obiekty klasy StringBuilder.

Język C# 2.0 traktuje łańcuchy znaków jako obiekty, na których możemy wykonywać przeróżne operacje związane z kontekstem łańcuchów znaków (sortowanie, przeszukiwanie etc.). Deklaracja klasy String jest następująca:

public class String : IComparable<T>, IEnumerable<T>, ICloneable
            {
                  Ciało_klasy
            }

Na początek ważna informacja do zapamiętania. A mianowicie taka, że każdy obiekt typu String to niezmienna sekwencja znaków w formacie Unicode. Ta „niezmienność” ma ogromne znaczenie. Gdy jakaś metoda zmienia łańcuch, to w rzeczywistości zwracana jest jego zmodyfikowana kopia. Oryginalny łańcuch znajduje się w pamięci, aż do momentu usunięcia go przez mechanizm za to odpowiedzialny (o usuwaniu obiektów z pamięci już pisaliśmy na łamach portalu CentrumXP). Taka sytuacja ma oczywiście wpływ na wydajność naszych programów. Jeśli więc w programie często manipulujemy na łańcuchach znaków, to rozsądnym wyborem w takiej sytuacji jest StringBuilder, o którym szczegółowo będzie w dalszej części artykułu.

Powyższy fragment kodu to definicja klasy String, która dziedziczy dobrze nam znane interfejsy. Jedynym nowym jest interfejs ICloneable, pozwalający tworzyć nowe egzemplarze o takiej samej wartości, co oryginalny obiekt. Innymi słowy, możemy utworzyć nowy obiekt, który będzie w sobie zawierał wartości oryginalnego łańcucha. Klasa String musi udostępniać metodę Clone(), której opis znajduje się właśnie w interfejsie ICloneable.

Najprostszym sposobem stworzenia łańcucha w języku C# 2.0 jest zainicjowanie zmiennej typu string i przypisanie jej w cudzysłowach odpowiednich znaków:

string s = "Mój pierwszy łańcuch znakowy";

Taki zbiór znaków nazywamy literałem znakowym. Łańcuchy mogą zawierać znaki ucieczki (np. „\n” – oznacza koniec wiersza lub „\t” – znak tabulacji). Bardzo często przy definicji łańcuchów programiści używają symbolu „@”. Oznacza on, że dany łańcuch jest traktowany przez kompilator w sposób dosłowny, np:

 string s1 = "C:\\NajlepszyPortal\\CentrumXP";
             string s2 = @"C:\NajlepszyPortla\CentrumXP";

W pierwszym wierszu zdefiniowaliśmy łańcuch s1, w którym ukośnik: „\” traktowany jest przez kompilator jako znak ucieczki (stąd został poprzedzony takim samym znakiem). Drugi łańcuch s2 traktowany jest przez kompilator już dosłownie. Dlatego też oba obiekty s1 i s2 są sobie równoważne.

Zanim napiszemy pierwszy przykład z użyciem łańchów znaków, przypomnijmy sobie metodę ToString(). Jak wiemy, metoda ta umożliwia nam tworzenie łańcuchów znaków z różnego rodzaju typów wbudowanych np:

            int i = 23;
            string lancuch = i.ToString();

Metoda ToString() zwraca obiekt typu String, który jest przypisywany do zmiennej lancuch.

Poniższy przykład prezentuje sposób tworzenia łańcuchów znaków:

public class Lancuchy
{
    static void Main()
    {
        //tworzymy lancuchy znakow
        string lancuchPierwszy = "Visual Studio 2005 Express";
        string lancuchDrugi = "SQL Server 2005 Express";
        string lancuchTrzeci = @"Programowanie
                                 jest
                                 super!!!";
        //przyklady kilku metod oraz wlasciwosci obiketow string
        if (string.Compare(lancuchPierwszy, lancuchDrugi, true) == 1)
        {
            Console.WriteLine("Łańcuch pierwszy jest większy od drugiego");
        }
           
        if (!string.IsNullOrEmpty(lancuchTrzeci))
        {
            int liczba = lancuchTrzeci.Length;
            Console.WriteLine("Łańcuch trzeci składa się ze {0} znaków.", liczba.ToString());
        }
 
        if (lancuchDrugi.StartsWith("SQL"))
        {
            Console.WriteLine("SQL Server 2005 jest najlepszy!");
        }
 
        if (lancuchTrzeci.EndsWith("super!!!"))
        {
            Console.WriteLine("Indeks: {0} to indeks wystąpienia znaku g w łańcuchu trzecim.", lancuchTrzeci.IndexOf('g'));
        }
    }
}

W powyższym przykładzie zdefiniowaliśmy 3 łańcuchy znaków, na których wykonaliśmy kilka standardowych metod i właściwości. Pierwszą z metod, jaką użyliśmy w powyższym programie jest metoda Compare(). Jest to przeciążona, publiczna metoda statyczna, która służy do porównywania dwóch łańcuchów znaków. Zwraca ona liczbę ujemną w przypadku, gdy pierwszy łańcuch jest mniejszy od drugiego, zaś dodatnią – gdy mamy sytuację odwrotną. Jeśli oba porównywane ze sobą łańcuchy mają tę samą długość, wówczas Compare() zwraca liczbę 0.

Drugą godną uwagi metodą jest EndsWith(). Metoda ta określa, czy dany łańcuch znaków pasuje do końcowego fragmentu tego łańcucha (jeśli tak, to metoda zwraca True).

W programie zastosowaliśmy również właściwości: IsNullOrEmpty (sprawdza, czy dany łańcuch jest pusty) oraz Length (określa liczbę znaków danego łańcucha).

Po uruchomieniu powyższego przykładu otrzymamy następujące wyniki:

Drugim punktem dzisiejszego tematu są podłańcuchy. Klasa String udostępnia metodę Substring(), która umożliwia programiście pobranie określonego podłańcucha z łańcucha znaków. Spójrzmy na poniższy program:

public class Lancuchy
{
    static void Main()
    {
        int index = 0;
        //tworzymy lancuch znakow
        string mojLancuch = "Programowanie w języku C# 2.0 jest super!!!";
        //wyswietlamy lancuch
        System.Console.WriteLine("Mój łańcuch znaków to: {0}.", mojLancuch);
        //pobiera lancuch ostatniego wystapienia w nim znaku: C
        index = mojLancuch.IndexOf('C');
        //tworzymy podlancuch
        string mojPodlancuch = mojLancuch.Substring(index);
        //wyswietlamy podlancuch
        System.Console.WriteLine("Mój pierwszy podłańcuch to: {0}.", mojPodlancuch);
        //tworzymy drugi podlancuch
        string drugiPodlancuch = mojPodlancuch.Substring(0, 6);
        System.Console.WriteLine("Język {0} jest najlepszy!!!", drugiPodlancuch);
    }
}

W powyższym przykładzie za pomocą metody Substring() utworzyliśmy 2 podłańcuchy. Stosowanie tej metody jest bardzo prostym mechanizmem. Metoda ta w pierwszej wersji przyjmuje jeden parametr (indeks, który określa miejsce rozpoczęcia pobierania podłańcucha), natomiast druga wersja tej metody przyjmuje 2 parametry (dodatkowo przyjmuje długość pobieranego podłańcucha). Obie te metody zostały wykorzystane w powyższym programiku, w wyniku którego otrzymamy następujące dane:

Zanim przejdziemy do bardzo ważnej klasy StringBuilder chcielibyśmy napisać parę słów na temat metody Split() oraz Trim(), które są przez programistów bardzo często używane w programach. Ta pierwsza zwraca podłańcuch ograniczony znakami w tablicy, natomiast ta druga usuwa z początku i końca łańcucha wszystkie wystąpienia określonych znaków. Aby bardziej przyswoić sobie wiedzę na ich temat, napiszmy następujący program:

public class Lancuchy
{
    static void Main()
    {
        //wlasciwosc Empty reprezentuje pusty string
        string napis = string.Empty;
        //tworzymy lancuch
        string mojLancuch = "    Split() - zwraca podłańcuch ograniczony znakami, króre określamy w tablicy   ";
        //wyswietlamy nasz string
        System.Console.WriteLine(mojLancuch);
        //tworzymy tablice stringow
        string[] tabString = new string[0];
        //Split() towrzy odpowiednie podlancuchy, ktore wrzucamy do tablicy
        tabString = mojLancuch.Split('-', ',');
        //przechodzimy po otrzymanej tablicy lancuchow
        foreach (string s in tabString)
        {
            napis += s.Trim() + "\n";
        }
        System.Console.WriteLine(napis);
    }
}

W powyższym przykładzie utworzyliśmy łańcuch znaków, który następnie wrzucamy w postaci odpowiednich podłańcuchów do tablicy stringów. Podłańcuchy te tworzymy za pomocą metody Split(), do której przekazujemy znaki (tablicę znaków rozdzielających). Są to znaki, które są miejscem podziału łańcucha. W naszym przypadku tymi znakami są myślnik oraz przecinek. Dlatego też w tablicy będą przechowywane 3 podłańcuchy. Obok metody Split() drugą bardzo często stosowaną metodą jest Trim(), która w naszym programie usuwa z początku i końca łańcucha niepotrzebne spacje.

Po uruchomieniu powyższego programiku otrzymamy następujące wyniki:

Na koniec opowiemy sobie o klasie StringBuilder. Klasa ta dostarcza nam szereg metod oraz właściwości, które pozwalają nam na tworzenie i modyfikowanie łańcuchów znaków. Najbardziej znaną metodą tej klasy jest Appena(), która dodaje łańcuch znaków na koniec aktualnego obiektu klasy StringBuilder. W przeciwieństwie do obiektów klasy String, obiekty StringBuilder podlegają zmianom. A więc modyfikacja obiektu StringBuilder nie tworzy jego kopii, ale zmienia aktualny łańcuch znaków.

Poniższy przykład prezentuje sposób użycia klasy StringBuilder:

public class Lancuchy
{
    static void Main()
    {
        //tworzymy obiekt klasy StringBuilder
        StringBuilder sb = new StringBuilder();
        //tworzymy lancuch
        string mojLancuch = "    Trim() - usuwa z początku i końca łańcucha znaki, króre określimy.  ";
        //wyswietlamy nasz string
        System.Console.WriteLine(mojLancuch);
        //tworzymy tablice stringow
        string[] tabString = new string[0];
        //Split() towrzy odpowiednie podlancuchy, ktore wrzucamy do tablicy
        tabString = mojLancuch.Split('-', ',');
        //przechodzimy po otrzymanej tablicy lancuchow
        foreach (string s in tabString)
        {
            sb.Append(s.Trim() + "\n");
        }
        System.Console.WriteLine(sb);
    }
}

W powyższym przykładzie utworzyliśmy obiekt sb klasy StringBuilder, za pomocą którego w łatwy sposób możemy tworzyć i modyfikować łańcuchy znaków. Obiekt ten wywołuje metodę Append(), dzięki której w sposób dynamiczny (za każdym razem na koniec danego łańcucha doklejany jest kolejny) tworzony jest nowy obiekt typu StringBuilder. Po skompilowaniu otrzymamy:

Dzisiaj opowiedzieliśmy sobie o łańcuchach znaków, które odgrywają w naszych programach bardzo ważną rolę. Programy bowiem bez nich nie mogą żyć. Dowiedzieliśmy się o obiektach klasy String oraz klasy StringBuilder. Poznaliśmy bardzo ważną różnicę między nimi oraz w oparciu o przykłady nauczyliśmy się ich stosować. Niniejszy artykuł to również miejsce definicji szeregu metod i właściwości, jakie towarzyszą na co dzień łańcuchom oraz ich podłańcuchom znaków.

Z łańcuchami znaków ściśle „współpracują” wyrażenia regularne, o których szerzej za tydzień.