MethodsCzęść NR.3Po krótkiej przerwie przypomniałem sobie o tym cyklu. Skończyłem już prawie kurs obiektowości w C# dlatego teraz mam nastrój na opisanie wszystkich metod z instancji klasy string.

Zrobiłem spis wszystkich metod w pierwszym wpisie tego cyklu. Nie zapominajcie dać +1 czy komentarz do tego wpisu.

Właściwość Length

Właściwość Length zwraca długość znaku i jest tylko do odczytu.

string s20  = "Czternaście";
int dlugos = s20.Length; //11

Jest to przydatna właściwość i zostanie użyta później w tym wpisie.

Metoda CompareTo()

Działanie tej metody jest podobne do metody statycznej “Compare”.

string s21  = "1";
object obj = "1";

int liczba = s21.CompareTo(obj); 
// zero bo zawartość jest taka sama 

Pomimo, iż w wersji przeciążanej możemy umieszczać obiekt. Obiekt musi być odpakowany jako string.

Metoda CopyTo()

Szczerze nie wiem czy ta metoda ma jakieś praktyczne zastosowanie ale cóż w tym wpisie nie jestem od tego. Skład metody wygląda tak:

public void CopyTo(int sourceIndex,char[] destination,
    int destinationIndex,int count)

Metoda ta potrzebuje tablicy znaków, do której znaki zostaną przegrane z naszego napisu string. SourceIndexto indeks pierwszego znaku, który ma być skopiowany. DestinationIndex dotyczy tablicy znaków czyli jest to indeks tablicy, od której znaki będą dodawane. Count oznacza liczbę znaków, która ma być skopiowana. Oto przykład użycia tej metody oraz jej wynik.

string s22 = "Whatever this";
string s23 = "For this";

char[] znaki = new char[10];

s23.CopyTo(0, znaki, 0, 3);
s22.CopyTo(4, znaki, 5, 4);

CopyTo()
Metoda ta jest bardzo delikatna, wystarczy podać niewłaściwy indeks i wszystko się rozsypie.

EndsWith()

Powoli przechodzę do poważniejszych metod. Metoda EndsWith() sprawdza czy napis kończy się podanym znakiem czy kolejnym napisem.

string s24 = "Memories óźą1";

bool[] konczySie = new bool[3];

konczySie[0] = s24.EndsWith("Mem");//false 

konczySie[1] = s24.EndsWith("óźą1");//true 

Metoda jest przeciążona i w innych wersjach może ignorować kulturę i ignorować wielkość znaków.

konczySie[2] = s24.EndsWith("ÓŹĄ1", true,
       System.Globalization.CultureInfo.CurrentCulture); 
//ignorowanie wielkości znaków 

Osobiście nie korzystałem z tej metody często i nie wiem jak kultura ma wpływ na metodę. MSDN nic mi nie powiedział. Ta czy siak poniższy kod zwraca zawsze bool, mimo, iż w tych pętlach foreach sprawdziłem wszystkie możliwe opcje.

//druga opcja metody ze wszystkimi możliwościami StringComparsion 
List<bool> konczySieLi = new List<bool>();

foreach (var item in Enum.GetValues(typeof(StringComparison)))
{
    konczySieLi.Add(s24.EndsWith("ą1", (StringComparison)item));
}

//trzecia opcja ze wszystkim kulturami 
List<bool> konczySieKultury = new List<bool>();

foreach (CultureInfo ci in CultureInfo.GetCultures(CultureTypes.AllCultures))
{
    konczySieKultury.Add(s24.EndsWith("óźą1", false, ci));
}

Jednak źle do tego podszedłem. Wiedziałem , że się mylę w końcu, gdyby to nie miało znaczenia to, po co miałbym tą możliwość do dyspozycji. Pomyślałem sobie może problem jest w tym ,że mówię programowi bezpośrednio aby szukał "óźą1" jako "óźą1".

Oto przykład gdzie wybór typu wyliczeniowego “StringComparsion” ma znaczenie. W napisie mam łacińską literę Æ i jest to ligatura liter ‘a’ i ‘e’. Często używana w językach staroangielskim, islandzkim, duńskim, norweskim i farerskim. Co oznacz , że w jakimś przypadku X nawet ta litera ma sens w informatycznym świecie.

string s24b = "Ͼ"; 
List<bool> konczySieLi = new List<bool>();

foreach (var item in Enum.GetValues(typeof(StringComparison)))
{
    konczySieLi.Add(s24b.EndsWith("ae", (StringComparison)item));
}

//Ordinal: false porównał string według porządku 
//OrdinalIgnore: false 
//reszta true 

W przykładzie z kulturami też otrzymałem parę wyrażeń false. Szkoda ,że mój kod jest uproszczony i nie pokazuje, dla której konkretnej kultury AE ,a Ænie są traktowane jako takie same.

Z innej strony jestem ciekawy jak porównywanie znaków działa skoro teraz wiem ,że są pojedyncze znaki, które mogą reprezentować dwa znaki. Wcześniej tego nie wiedziałem, czyniąc powtórkę wiedzy dla mnie przyjemną.

Ta metoda ma jednak sensowne użycie np. dzięki niej możesz sprawdzać typy plików skoro ich typ jest zawsze opisany na końcu.

Metoda Insert()

Metoda ta wstawia nowe symbole do istniejącego napisu string.

string s25 = "Urodzony dla miasta";
string s26 = s25.Insert(9, "by zostać programistą ");

Może chcesz przechowywać rezultat tej metody w formie obrazka.

Insert() string
Niema tutaj dużej filozofii trzeba tylko podać odpowiedni numer indeksu, gdzie napis może być wstawiony.

Metoda LastIndexOf()

Gdy chcemy ustalić w którym miejscu dokładnie pojawia się dany symbol ostatni raz to używamy tej metody. Metoda ta ma jest metodą przeciążoną i posiada kilka wersji . Opisałem jest wszystkie w kodzie poniżej.

string s27 = "Objectioo##oo##oo##oon, Hold It, Gotcha";
string po0 = "01234567890";
string po1 =            "1234567890";
string po2 =                      "1234567890";
string po3 =                                "123456789";

int[] rez = new int[5];

rez[0] = s27.LastIndexOf('o'); //34 
rez[1] = s27.LastIndexOf("oo"); //19 

rez[2] = s27.LastIndexOf('a',20); 
//sprawdza znaki od 20 
//nie znajdzie litery 'a' ponieważ jest ona na końcu napisu. 
// -1 jest zwracane w przypadku nie zalezienia symbolu 

rez[3] = s27.LastIndexOf('c',5,2); 
//sprawdza znak 5 i 4 
//znak 'c' jest 4 w tym zakresie 

rez[4] = s27.LastIndexOf("##",13, 5);
//sprawdza znaki 13,12,11,10,9 
// "##" występuje w symbolach 10,9 
//zwróci 9 

Jest ona użyteczna. Jednak do wyszukiwania napisu w napisie bardziej użyteczne są wyrażenia regularne.
W połączeniu z innymi metodami jedna z najbardziej użytecznych metod w instancji klasy string.

Metoda PadLeft() i PadRight()

Metody te dodają wolną przestrzeń po lewej albo po prawej stronie. Małym problem stanowi fakt ,że liczba, którą podajemy w metodzie to przyszła całkowita liczba znaków. Czyli jeśli podamy liczbę mniejszą bądź równą liczbie znaków tego napisu metody te nie dodadzą żadnej przestrzeni. Oczywiście nie musimy liczyć liczby znaków napisu do tego mamy właściwość “Lenght”.

Przykładowo jeśli chcę przesunąć napis o 4 miejsca po prawej używam metody “PadRight” i jako argument podaję długość napisu plus ilość znaków, o ile chce go przysunąć.

W drugiej wersji tej metody mogą podać jeden znak, który będzie grał role znaku odstępu.

string s28 = "Cezary Walenciuk";

string[] rezName = new string[5];
rezName[0] = s28.PadLeft(30);
rezName[1] = s28.PadLeft(30, '+');
rezName[2] = s28.PadRight(s28.Length + 14);
rezName[3] = s28.PadRight(s28.Length + 14, '_');

rezName[4] = s28.PadLeft(s28.Length + 4, '*');
rezName[4] = rezName[4].PadRight(rezName[4].Length + 4, '*');


padleft padright
Warto zwrócić uwagę na to ,że alternatywne symbole inaczej wydłużają napis chociaż widać to na notatce ale w konsoli nie powinno tak być.

Ostatni przykład kodu pokazuje jak efektownie użyć tych metod aby otoczyć napis gwiazdkami albo innym znakiem.

Metoda Remove()

Kolejna pożyteczna metoda. Usuwa ona napis od określonego znaku albo od "+ ilość znaków". Ostatnio korzystałem z tej metody aby tworzyć określoną ścieżkę.

string s29 = @"D:\Projekty\Blog\Program.cs";
string[] rezPath = new string[2]; 

rezPath[0] = s29.Remove(11);
rezPath[1] = s29.Remove(11, 5);

Za pierwszym razem metoda usuwa znaki od jedenastego indeksu ,a potem od jedenastego pięć znaków

Remove String
Kolejna metoda, która działa dobrze w połączeniu z metodami, które wyszukują określone indeksy.

Metoda Split()

Ha! Tutaj zaczyna się zabawa.

Split() pozwala na rozdzielenie łańcucha znaków na podłańcuchy. Trzeba tylko podać znak bądź znaki, po których napis ma być podzielony i usunięty. Starałem się jak najlepiej przedstawić działanie tej metody. Posiada ona kilka wersji i przedstawiłem je wszystkie. Do kodu dałem komentarze aby uniknąć dzielenia bloku kodu.

string s30 = "Logic Error,Human Error,Error Analysis," 
+ "Observational Error,Popular Error,Refractive Error ";

//1: params chars string[] rezSplit0 = s30.Split(' ');
string[] rezSplit1 = s30.Split(' ', ',');

//2: parmas chars,int count 
char[] znaki = new char[] {' ', ',' , 'E' };
string[] rezSplit2 = new string[5];
rezSplit2 = s30.Split(znaki, 5); //ostatni 5 string w tablicy 

//3: paramas chars,StringSplitOption
//tablica nie będzie zawierać pustych symboli czyli spacji 
//w poprzednim przykładzie tak było 
string[] rezSplit3 = 
s30.Split(znaki,StringSplitOptions.RemoveEmptyEntries);

//4: string[], StringOptions 
string[] nap = new string[] { "Error," };
string[] rezSplit4 = s30.Split(nap, StringSplitOptions.None);

//6: string[], int count, StringOption 
string[] rezSplit6 = new string[2];
rezSplit6 = s30.Split(nap, 2, StringSplitOptions.None);

//5: char[],int count,StringOption 
string[] rezSplit5 = new string[4];

rezSplit5 = s30.Split
(znaki, 4, StringSplitOptions.RemoveEmptyEntries);

Przedstawienie rezultatów tej metody było trochę kłopotliwe. Zaznaczyłem na żółto każdą tablice, chociaż teraz nie wiem czy to był dobry pomysł.

Split() string2
W tablicy drugiej “rezSplit2” napis był dzielony aż do piątego miejsca w tablicy, gdzie operacja dzielenia łańcucha została już zakończona, widać to na rysunku powyżej. Każda następna metoda przeciążona z tym parametrem ma to samo zachowanie patrz 5 i 6.

Trzecia tablica nie posiada pustych symboli, jak to widać w tablicy drugiej dzięki argumentowi: “StringSplitOptions.RemoveEmptyEntries”.

Miałem też problemem z dodawaniem parametrów znaków, czy string do metody. Jeśli zamierzasz użyć wersji przeciążonej, która wymaga czegoś więcej niż tablicy char czy string, musisz stworzyć taką tablicę przed metodą. Jeśli tak nie jest możesz podać parametry po kolei jak to widać w rezSplit1.Jest to możliwe dzięki słowu kluczowemu params.

Split() string3

Zerowa tablica przechowuje łańcuchy, które są wynikiem podzielenia dużego napisu s30 za każdym razem gdy program napotkał znak spacji.

Pierwsza tablica jest wynikiem podzielenia od dwóch znaków: spacji i przecinka. Kolejność ma znaczenie chociaż dla znaków nic to nie zmieni, dla łańcuchów znaków już tak. Próbowałem sprawdzić wpisując dwa łańcuchy “Error” i “Error,” ponieważ “Error” zawsze został znajdowany wcześniej w ten sposób nie osiągnąłem zamierzonego efektu. Chciałem podzielić napis za każdym razem gdy pojawiał się “Error” z przecinkiem bądź bez.

Jeśli spojrzysz na napis s30 i na przykład ztablicy 4 (rysunek wyżej) zobaczysz , że dwa “Error-y” zostały ponieważ nie mają one pod koniec przecinka. Aby go też usunąć kolejność tablicy argumentów musiała być następująca “Error” ,a potem “Error,”. Dla lepszego efektu jeszcze do listy argumentów dodałem przecinek i spacje aby ich się też pozbyć.

//4: string[], StringOptions 
string[] nap2 = new string[] { "Error","Error,",","," " };

string[] rezSplitBB = 
s30.Split(nap2, StringSplitOptions.RemoveEmptyEntries);

Używając odpowiednich argumentów i ich kolejności możesz osiągnąć odpowiedni efekt. Jednak nie oszukujmy się ,że żeby używać dobrze tej metody dynamicznie trzeba zakładać jakiś schemat znaków dla napisu.
Split() string 4

Tak jak powiedziałem wcześniej metoda ta używa słowa params . Pozwala ona w ten sposób na użycie pętli foreach bez deklaracji tablicy.

foreach (var item in s30.Split(' ',','))
{
    Console.WriteLine(item);
}

Warto też podkreślić, że metoda ta nie jest wydajna, możesz stworzyć naprawdę dużą tablicę napisów string. Tablica nie jest dobrym magazynem na typy referencyjne ,a string właśnie jest tym typem. Patrząc na tę metodę rozumiem dlaczego powstała klasa StringBuilder ale o tym później.

Metoda StartsWith()

Metoda sprawdza czy dany napis zaczyna się danym napisem. Za przykład wziąłem parę łańcuchów znaków z mojego ostatniego niebieskiego ekranu śmierci.

string[] s31 = new string[] {
    "WARNING: Unable to verify timestamp for ndis.sys",
    "ERROR: Module load completed but symbols could not be loaded for ndis.sys",
    "WARNING: Unable to verify timestamp for athrusb.sys",
    "ERROR: Module load completed but symbols could not be loaded for athrusb.sys",
    "WARNING: Unable to verify timestamp for Wdf01000.sys",
    "ERROR: Module load completed but symbols could not be loaded for Wdf01000.sys",
    "WARNING: Unable to verify timestamp for USBPORT.SYS",
    "ERROR: Module load completed but symbols could not be loaded for USBPORT.SYS",
    "Probably caused by : nwifi.sys ( nwifi+4860 )"};

List<string> lista = new List<string>();

for (int i = 0; i < s31.Length; i++)
{
    if (s31[i].StartsWith("ERROR:"))
    {
        lista.Add(s31[i]);
    }
}

Potem w pętli for sprawdzam tablice komunikatów i dodaje do listy te które zaczynają się od ”ERROR:”.
Jak widzisz metoda ta może się sprawdzić do przeszukiwania logów.
Metoda ta ma zbliżone opcje do metody “EndsWith()”. Nie chce się powtarzać więc w przypadku innych pytań odsyłam ciebie do nagłówka “EndsWith()” wyżej.

Metoda Substring()

Jeśli śledzisz ten wpis w kolejności jaką ja ustaliłem widziałeś już metody jak Remove() i Split().

Metoda Substring służy do wycinania napisu. Jednak być może do swoich celów uznasz ,że wcześniejsze metody są bardziej efektowne.

string s33 = "Pan Niebieski";
string[] rezSubs = new string[2];
            
//Rezultat: Niebieski 
rezSubs[0] = s33.Substring(4);

//od 4 znaku wycina 
//Rezultat: Pan 
rezSubs[1] = s33.Substring(0, 4); // od 0 do 4 

Metoda ta jest jednak kiepska. Muszę w końcu podawać numery indeksów ,aby wyciąć napis. W dynamicznym napisie musiałbym ten indeks ustalać za pomocą metod jak “LastIndexOf()”.

Metoda ToCharArray()

Sam napis string to tablica znaków co oznacza ,że mogę na niej użyć pętliforeach,(interface IEnumerable) czy traktować jak tablice.

string s34a = "!)(*";

foreach (var item in s34a )
{ 
    Console.WriteLine(item);
}

char wykrzynki = s34a[0];

Jednak typ string to nie tablica znaków char[] dosłownie.

string to nie char[]
Dlatego do takich sytuacji jest metoda ToCharArray().

string s34 = "@#$%^&";
char[] znaki3 = s34.ToCharArray();
char[] znaki4 = s34.ToCharArray(4, 2); //^&

W wersji drugiej tej metody mogę określić, od którego znaku, do którego ma rozpocząć się kopiowanie.

Teraz zapewne się zastanawiasz jaka jest różnica pomiędzy char[] a string. Szczerze nigdy się na tym nie zastanawiałem. Być może przetrzymywanie znaków w char[] bardziej zajmuje stertę chociaż to tylko moja teoria.

Nie jestem niczego pewien ,a Internetu i książki, które czytałem nie poruszały tego problemu. A nie chcę uznawać automatycznie , że to co przeczytałem o C++ może też się odnieść do C#. Chociaż na pewno char[] przechowuje inaczej znaki niż string i ma to coś wspólnego z Unicode.

String jest typem zarządzanym i nie musisz się martwić o jej wielkość w przeciwieństwem do tablic char[]. Prawdopodobnie operacje na typie string są bardziej wydajne niż na tablic char.

Tak przy okazji gdybyś chciał zmienić tablice znaków nastring można to zrobić w ten sposób.

string s34b = new string(znaki3);

Metoda ToLower() i ToUpper()

Oto metody, które służą do powiększania znaków bądź ich pomniejszania. Powiększanie znaków jest zależne od kultury chociaż ja nie znalazłem dziury w działaniu tej metody, mimo iż wypróbowałem wszystkie kultury.

Metody te posiadają swoje mutacje z “Invariant” , które zachowują zachowanie danej kultury. Chociaż nie widzę jak te zachowania mogą być utracone nawet używając łacińskich liter.

string s35 = "SiŁtAŚnIe lUBie tEn WpIs i dAM +1";
string[] rez_To = new string[4];

rez_To[0] = s35.ToLower();
rez_To[1] = s35.ToUpper();
rez_To[2] = s35.ToLowerInvariant();

//nie zmienia kultury 
rez_To[3] = s35.ToUpperInvariant();

//nie zmienia kultury 
s35 = "ą ć ź ż ś ę ń € ó ł œ æ: ";

//sprawdzanie rezultatu ToUpper dla wszystkich kultur 

List<string> list_To = new List<string>();

foreach (CultureInfo ci in 
CultureInfo.GetCultures(CultureTypes.AllCultures))
{
    list_To.Add(s35.ToUpper(ci));
}

ToLower i ToUpper

Nie widzę powodu aby dalej opisywać tę metodę.

Metoda Trim()

Domyślnie ta metoda usuwa białe znaki ,ale może też usuwać wszystkie inne znaki jakie podamy. Jednak ta metoda usuwa białe znaki dopóki nie napotka innego znaku. Co to znaczy?.

string s36 = " ***___FILM _*w*_ 3D___*** ";
string[] rez_Trim = new string[3];

rez_Trim[0] = s36.Trim();
rez_Trim[1] = s36.Trim('_');
rez_Trim[2] = s36.Trim('_', ' ','*');

Trim() string

Znaczy to ,że białe znaki mogą zostać usunięte tylko z początku i z końca napisu , jak to widać w rez_Trim[0].

Trim za drugim razem w ogóle nie zadziałało ponieważ tym razem znak spacji jest traktowany jako normalny znak ,a znak, który ma być usunięty "*’ nie jest na początku i na końcu napisu.

Za trzecim razem podałem listę argumentów i może być ona nieskończona ponieważ metoda przyjmuje je jako tablicę i używa słowa kluczowego params. Efekt jest zadowalający. Jednak wciąż znaki na środku napisu nie zostały usunięte.

Co dalej:

Czuje się już trochę zmęczony a ten post jest już za długi, chociaż niewiele już zostało do opisania wszystkich metody string. Wolę jednak zostawić to na później i opublikować moją obecną pracę już teraz.