Pouze tento týden sleva až 80 % na e-learning týkající se Javy. A zároveň využij akce až 30 % zdarma při nákupu e-learningu - Více informací.
Hledáme koordinátorku kurzů a programátora, 100% home office, 100% flexibilní. Prozkoumej aktuální pozice
discount week 30 - hiring

Lekce 11 - Textové řetězce v C# do třetice - Split a Join

V předešlém cvičení, Řešené úlohy k 10. lekci C# .NET, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

Dnes si vysvětlíme další metody na řetězci, které jsem vám záměrně zatajil, protože jsme nevěděli, že string je vlastně pole :)

Na řetězci můžeme používat mnoho metod, které známe z pole. Jsou to např: First(), Last(), IndexOf() a další.

Když si vytvoříme libovolnou proměnnou a napíšeme za ni tečku, Visual Studio nám zobrazí nabídku všech metod a vlastností (a také proměnných, ale k tomu se dostaneme až u objektů), které na ni můžeme volat. Tomuto nástroji se říká IntelliSense a zpříjemní nám práci s kódem, který za nás doplňuje. Zkusme si to:

Metody na textovém řetězci string ve Visual Studio

Tu samou nabídku lze vyvolat také stiskem Ctrl + Mezerník v případě, že textový kurzor umístíme na tečku. Samozřejmě to platí pro všechny proměnné i třídy a budeme toho využívat stále častěji. Metody jsou řazené abecedně a můžeme jimi listovat pomocí kurzorových šipek. VS nám zobrazuje popis metod (co dělají) a jaké vyžadují parametry.

Řekněme si o následucících metodách a ukažme si je na jednoduchých příkladech:

Další metody na řetězci

Insert()

Vloží podřetězec do řetězce na určitou pozici. Parametry jsou pozice v řetězci a podřetězec.

Console.WriteLine("Punk's dead".Insert(7, "not "));

Výstup:

Konzolová aplikace
Punk's not dead

Remove()

Vymaže znaky od dané pozice do konce. Parametrem je číselná pozice. Můžeme zadat druhý parametr, kterým je počet znaků, které chceme vymazat.

Console.WriteLine("Michael Jackson".Remove(7, 5));

Výstup:

Konzolová aplikace
Michaelson

Substring()

Vrátí podřetězec od dané pozice do konce řetězce. Můžeme zadat druhý parametr, kterým je délka podřetězce.

Console.WriteLine("Wolfgang Amadeus Mozart".Substring(9, 7));

Výstup:

Konzolová aplikace
Amadeus

CompareTo()

Umožňuje porovnat dva řetězce podle abecedy. Vrací -1 pokud je první řetězec před řetězcem v parametru, 0 pokud jsou stejné a 1 pokud je za ním:

Console.WriteLine("Argentina".CompareTo("Barbados"));
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Výstup:

Konzolová aplikace
-1

Pojďme se nyní podívat na 2 další metody na stringu, které jsou opravdu velmi užitečné.

Split() a Join()

Z předchozího tutoriálu víme, že parsování řetězce po znaku může být někdy docela složité a to jsme dělali poměrně jednoduchý příklad. S řetězci se samozřejmě budeme setkávat stále, a to jak na vstupu od uživatele (např. z konzole nebo z polí v okenních aplikacích), tak v souborech TXT a XML. Velmi často máme zadán jeden delší string (řádek souboru nebo řádek konzole), ve kterém je více hodnot, oddělených tzv. separátory, např. čárkou. V tomto případě hovoříme o formátu CSV (Comma-Separated Values, tedy hodnoty oddělené čárkou). Abychom si byli jisti, že víme, o čem hovoříme, ukažme si nějaké ukázkové řetězce:

Jan,Novák,Dlouhá 10,Praha 3,130 00
.. ... .-.. .- -. -.. ... --- ..-. -
(1,2,3;4,5,6;7,8,9)
  • První řetězec je očividně nějaký uživatel, takto bychom mohli např. realizovat uložení uživatelů do CSV souboru, každý na jeden řádek.
  • Druhý řetězec jsou znaky Morseovy abecedy, separátor (oddělovač) je zde mezera.
  • Třetí řetězec je matice o 3 sloupcích a 3 řádcích. Oddělovač sloupců je čárka, řádků středník.

Na stringu můžeme volat metodu Split(), která bere jako parametr separátor (typu char), případně dokonce pole separátorů. Následně původní řetězec rozdělí podle separátorů na pole podřetězců, které vrátí. To nám velmi ulehčí práci při rozdělování hodnot v řetězci.

Metoda Join() se volá přímo na typu string a umožňuje nám naopak pole podřetězců spojit oddělovačem do jediného řetězce, parametry jsou oddělovač a pole. Výstupem metody je výsledný řetězec.

Jelikož neumíme tvořit objekty (uživatele) a ani pracovat s vícerozměrnými poli (matice), zkusíme si naprogramovat dekodér zpráv z Morseovy abecedy.

Dekodér Morseovy abecedy

Pojďme si opět připravit strukturu programu. Budeme potřebovat 2 řetězce se zprávou, jeden se zprávou v Morseově abecedě, druhý zatím prázdný, do kterého budeme ukládat výsledek našeho snažení. Dále budeme jako v případě samohlásek potřebovat nějaký vzor písmen. K písmenům samozřejmě vzor jejich znaku v morzeovce. Písmena můžeme opět uložit do pouhého stringu, protože mají jen jeden znak. Znaky Morseovy abecedy mají již znaků více, ty musíme dát do pole.

Struktura našeho programu by nyní mohla vypadat následovně:

// řetězec, který chceme dekódovat
string s = ".-.. . --- -. .- .-. -.. ---";
Console.WriteLine("Původní zpráva: {0}", s);
// řetězec s dekódovanou zprávou
string zprava = "";

// vzorová pole
string abecedniZnaky = "abcdefghijklmnopqrstuvwxyz";
string[] morseovyZnaky = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....",
"..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-",
"...-", ".--", "-..-", "-.--", "--.."};

Můžete si potom přidat další znaky jako čísla a interpunkční znaménka, my je zde vynecháme.

Rozdělení řetězce na pole metodou Split()

Nyní si řetězec s rozbijeme metodou Split() na pole podřetězců, obsahujících jednotlivé znaky morzeovky. Splitovat budeme podle znaku mezery. Pole následně proiterujeme cyklem foreach:

// rozbití řetězce na znaky morzeovky
string[] znaky = s.Split(' ');

// iterace znaky morzeovky
foreach (string morseuvZnak in znaky)
{

}

Nalezení odpovídajícího písmena

V cyklu se pokusíme najít aktuálně čtený znak morzeovky v poli morseovyZnaky. Bude nás zajímat jeho index, protože když se podíváme na ten samý index v poli abecedniZnaky, bude tam odpovídající písmeno. To je samozřejmě z toho důvodu, že jak pole tak řetězec obsahují stejné znaky, seřazené dle abecedy. Umístěme do těla cyklu následující kód:

char abecedniZnak = '?';
int index = Array.IndexOf(morseovyZnaky, morseuvZnak);
if (index >= 0) // znak nalezen
    abecedniZnak = abecedniZnaky[index];
zprava += abecedniZnak;

Kód nejprve do abecedního znaku uloží '?', protože se může stát, že znak v naší sadě nemáme. Následně se pokusíme zjistit jeho index. Pokud se to podaří, dosadíme do abecedniZnak znak z abecedních znaků pod tímto indexem. Nakonec znak připojíme ke zprávě. Operátor += nahrazuje zprava = zprava + abecedniZnak.

Dokončení

Závěrem samozřejmě zprávu vypíšeme a přidáme ReadKey():

Console.WriteLine("Dekódovaná zpráva: {0}", zprava);
Console.ReadKey();

Výsledek:

Konzolová aplikace
Původní zpráva: .-.. . --- -. .- .-. -.. ---
Dekódovaná zpráva: leonardo

StringSplitOptions

Ideálně bychom se měli nějak vypořádat s případy, kde uživatel zadá např. více mezer mezi znaky (to uživatelé rádi dělají). Split() poté vytvoří o jeden řetězec v poli více, který bude prázdný. U metody Split() můžeme jako druhý parametr předat StringSplitOptions.RemoveEmptyEntries, díky kterému se ve vráceném poli nebudou nacházet prázdné řetězce. V tomto případě však musíme separátor v prvním parametru předávat jako pole. Splitování by tedy vypadalo takto:

string[] znaky = s.Split(new char[]{' '}, StringSplitOptions.RemoveEmptyEntries);

Hotovo! Za úkol máte si naprogramovat program opačný, který naopak zakóduje řetězec do morzeovky, kód bude velmi podobný. Se Split() a Join() se potkáme během C# kurzu ještě několikrát.

Speciální znaky a escapování

Textový řetězec může obsahovat speciální znaky, které jsou předsazené zpětným lomítkem \. Je to zejména znak \n, který kdekoli v textu způsobí odřádkování a poté \t, kde se jedná o tabulátor.

Zpětné lomítko \ napíšeme na české klávesnici stiskem Pravého Alt + Q:

Zpětné lomítko na české klávesnici

Pojďme si to vyzkoušet:

Console.WriteLine("První řádka\nDruhá řádka");
Console.ReadKey();

Znak \ označuje nějakou speciální sekvenci znaků v řetězci a je dále využíván např. k psaní unicode znaku jako \uxxxx, kde xxxx je kód znaku.

Problém může nastat ve chvíli, když chceme napsat samotné \, musíme ho tzv. odescapovat:

Console.WriteLine("Toto je zpětné lomítko: \\");

Stejným způsobem můžeme odescapovat např. uvozovku tak, aby ji C# nechápal jako konec řetězce:

Console.WriteLine("Toto je uvozovka: \"");

Problém může být, když chceme zapsat nějakou delší cestu k souboru, kde máme velké množství zpětných lomítek. I na to v Microsoftu mysleli a zavedli modifikátor @.

Jak již asi víte, na klávesnici zavináč napíšeme pomocí Pravého Alt + V:

Zavináč

Díky tomuto modifikátoru C# automaticky escapuje celý námi napsaný řetězec v kódu:

Console.WriteLine(@"C:\Users\sdraco\Dropbox\itnetwork");

Vstupy z konzole a polí v okenních aplikacích se samozřejmě escapují sami, aby uživatel nemohl zadat \n a podobně. V kódu to má programátor povoleno a musí na to myslet.

V následujícím cvičení, Řešené úlohy k 11. lekci C# .NET, si procvičíme nabyté zkušenosti z předchozích lekcí.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 1217x (133.2 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

Předchozí článek
Řešené úlohy k 10. lekci C# .NET
Všechny články v sekci
Základní konstrukce jazyka C# .NET
Přeskočit článek
(nedoporučujeme)
Řešené úlohy k 11. lekci C# .NET
Článek pro vás napsal David Čápka
Avatar
Uživatelské hodnocení:
87 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 13 let. Má rád Nirvanu, sushi a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity

 

 

Komentáře
Zobrazit starší komentáře (68)

Avatar
David Holohlavský:12.3.2020 22:40

Díky za článek. ;-)

 
Odpovědět
12.3.2020 22:40
Avatar
Marek Vajčner:28.3.2020 12:15

Bezva, další zajímavé metody. Tak tedy jdu na další úlohy. Jsem zvědav jak moc ty řetězce v polích "zvořu".

 
Odpovědět
28.3.2020 12:15
Avatar
Petr Šenfeld:26.6.2020 15:21

asi hodně pozdní odpověď ale myslím, že by to mělo jít po této úprávě:

string[] znak = s.Split('?');

  • bych smazal, není potřeba splitovat slovo a už vůbec ne podle '?'

string morseuvZnak = "";

  • bys měl doplnit "?", jinak pokud znak nenajde tak nic nedoplní.

zzp += morseuvZnak.ToCha­rArray();

  • bych předělal na zzp += (morseuvZnak + " ");

mohu se mýlit, taky se vzdělávám :-)

Editováno 26.6.2020 15:23
 
Odpovědět
26.6.2020 15:21
Avatar
popo49
Člen
Avatar
popo49:14.12.2020 17:08

Překlad do Morseovky po mém :-)

// zjistím pozici znaku v datovém typu string
       static int PoziceZnaku(string znaky, char c)
       {
           int pozice = 0;
           for (int j = 0; j < znaky.Length; j++)
           {
               if (c == znaky[j])
               {
                   pozice = j;
                   break;
               }
               else
                   pozice = 0;
           }
           return pozice;
       }
       static void Main(string[] args)
       {
           string s = "morseova abeceda"; //string k překladu
           Console.WriteLine(s);
           s = s.ToLower(); //převod na malá písmena
           string[] preklad = new string[s.Length]; //pole pro ukládáni morseovych znaků
           // definované znaky
           string abecedniZnaky = "abcdefghijklmnopqrstuvwxyz";
           string[] morseovyZnaky = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....",
                   "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-",
                   "...-", ".--", "-..-", "-.--", "--.."};
           char odelovac = ','; //použití v konečném výpisu (pro oddělení znaků)
           int i = 0; //iterace překládaných znaků
           int j = 0; //iterace nedefinovaných znaků

           //průchod vetou pro překlad po znacích
           foreach (char c in s)
           {
               if (abecedniZnaky.Contains(c)) //kontrola zda je znak definován
               {
                   int index = PoziceZnaku(abecedniZnaky, c); //nešel použít IndexOf() - proto použita vlastní metoda
                   preklad[i] = morseovyZnaky[index];
                   if ((i+1) < (s.Length - j)) //ve výpise chci oddělit jednotlivé znaky ',' - za posledním znakem oddělovač nechci
                       Console.Write("{0}{1}", preklad[i], odelovac);
                   else
                       Console.Write("{0}", preklad[i]);
                   i++;
               }
               else
                   j++; //pokud není definovaný
           }
           Console.WriteLine();
           Console.ReadKey();
       }
 
Odpovědět
14.12.2020 17:08
Avatar
Terka Muffy Kalivodová:15.4.2021 21:50

Mám dotaz k StringSplitOp­tions.RemoveEm­ptyEntries. V článku píšete, že musíme první parametr předávat jako pole, tedy:
string[] znaky = s.Split(new char[]{' '}, StringSplitOp­tions.RemoveEm­ptyEntries);
ale mě funguje i tento zápis:
string[] znaky = s.Split((' '), StringSplitOp­tions.RemoveEm­ptyEntries);
Proč to tedy komplikovat, když funguje jednoduchá verze?

 
Odpovědět
15.4.2021 21:50
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Alesh
Překladatel
Avatar
Odpovídá na Terka Muffy Kalivodová
Alesh:15.4.2021 23:56

Pokud se nepletu, tak ten druhý zápis je možný až od vyšší verze .NET, doma ti to fungovat bude, ale tady ne, schválně si vyzkoušej obě možnosti v některé z těch konzolí v článku, kde se demonstruje nějaký kód. Prostě do toho klikni, kód pak můžeš libovolně upravovat. Čili pak bacha, pokud to bude ve cvičeních k této lekci, tak zapisovat jedině tím prvním způsobem. ;-)

 
Odpovědět
15.4.2021 23:56
Avatar
Vaclav Klimes:2.6.2021 9:29

Ahoj, můžete mně někdo pomoci pochopit, co dělám špatně? Níže uvedený kód při běžném spuštění (F5) se zastaví na prvním řádku s chybou "System.NullRe­ferenceExcepti­on: Odkaz na objekt není nastaven na instanci objektu.", ale při krokovém (F11) bez obtíží funguje..Moc díky.

string[] pozice = lokace.message.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
                    place = pozice[0];
                    popis = pozice[2];
                    nazev = pozice[1];
                    for (int i = pozice.Length - 1; i > 2; i--)
                        smery += pozice[i] + " ";
                    Console.WriteLine("Můžeš jít na {0}", smery);
                    Console.WriteLine("Zadej příkaz: ");
                    choice = Console.ReadLine().ToLower().Trim();
 
Odpovědět
2.6.2021 9:29
Avatar
Luboš Marvan
Redaktor
Avatar
Luboš Marvan:29.10.2021 9:30

Ahoj,
v úvodu je pojednání o metodách First(), Last(), IndexOf() stejných na polích a stringu. Mě ale připadá, že IndexOf() není. Ve stringu volám metodu na proměnné (instanci), zatímco u pole to volám na třídě Array a proměnná pole je jako parametr.
Nebo dělám někde chybu?
Luboš

Odpovědět
29.10.2021 9:30
Jsi tam, kde jsou tvoje myšlenky
Avatar
Eva Šimerková:21.11.2021 17:54

Chybí mi zde řešení úkolu na program, který zakóduje řetězec do morseovky...

 
Odpovědět
21.11.2021 17:54
Avatar
Ladislav SKOKAN:2. ledna 15:55

pokud někomu nefunguje IndexOff, zkuste upravit druhý parametr
index = Array.IndexOf(a­becedniZnaky, vstup[i].ToStrin­g());

 
Odpovědět
2. ledna 15:55
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 78. Zobrazit vše