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é jsme zatím
záměrně neřešili, protože jsme dosud 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é můžeme na proměnné 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:
Tutéž nabídku lze vyvolat také stiskem kláves Ctrl + Mezerník v případě, že textový kurzor umístíme za tečku. Tento postup samozřejmě platí pro všechny proměnné i třídy a budeme ho využívat stále častěji. Metody jsou řazené abecedně a můžeme jimi listovat pomocí kurzorových šipek. VS nám zobrazuje, co metody dělají (jejich popis) a jaké vyžadují parametry.
Nyní si něco řekneme o následujících metodách a ukážeme si je na jednoduchých příkladech:
Další metody na řetězci
Insert()
Vloží podřetězec na určitou pozici do řetězce. Parametry jsou pozice v řetězci a podřetězec.
{CSHARP_CONSOLE}
Console.WriteLine("Punk's dead".Insert(7, "not "));
{/CSHARP_CONSOLE}
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.
{CSHARP_CONSOLE}
Console.WriteLine("Michael Jackson".Remove(7, 5));
{/CSHARP_CONSOLE}
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.
{CSHARP_CONSOLE}
Console.WriteLine("Wolfgang Amadeus Mozart".Substring(9, 7));
{/CSHARP_CONSOLE}
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 první řetězec za řetězcem v
parametru:
{CSHARP_CONSOLE}
Console.WriteLine("Argentina".CompareTo("Barbados"));
{/CSHARP_CONSOLE}
Výstup:
Konzolová aplikace
-1
Pojďme se nyní podívat na dvě další metody na řetězci typu
string
, 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é. Přitom jsme prováděli 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 zadaný 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
jistí, že víme, o čem hovoříme, ukažme si některé 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átorem (oddělovačem) je zde mezera.
- Třetí řetězec je matice o 3 sloupcích a 3 řádcích. Oddělovačem sloupců je čárka, řádků středník.
Na řetězci typu string
můžeme volat metodu
Split()
, která jako parametr bere 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 řetězci
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) ani pracovat s vícerozměrnými poli (maticemi), 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 dva
řetězce se zprávou: jeden s
se zprávou v Morseově abecedě,
druhý zprava
zatím prázdný. Do druhého budeme ukládat
výsledek našeho snažení.
Dále budeme potřebovat nějaký vzor písmen. Uložíme si
ho do řetězce abecedniZnaky
typu string
, protože
každé písmeno má jen jeden znak. K písmenům v řetězci potřebujeme
vzor jejich znaku v morseovce. Symboly Morseovy abecedy oproti
písmenům obsahují znaků více, proto si je dáme do pole
morseovyZnaky
typu string
.
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 = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."};
Samozřejmě je možné přidat i další znaky jako čísla nebo interpunkční znaménka, my je zde však 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 morseovky. Splitovat budeme
podle znaku mezery. Pole následně proiterujeme cyklem
foreach
:
// rozbití řetězce na znaky morseovky string[] znaky = s.Split(' '); // iterace znaků morseovky foreach (string morseuvZnak in znaky) { }
Nalezení odpovídajícího písmena
V cyklu se pokusíme najít aktuálně čtený znak morseovky v poli
morseovyZnaky
. Bude nás zajímat jeho index,
protože když se podíváme na tentýž 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í
Na závěr samozřejmě zprávu vypíšeme a přidáme
ReadKey()
:
{CSHARP_CONSOLE}
// ř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 = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....",
"..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-",
"...-", ".--", "-..-", "-.--", "--.."};
// rozbití řetězce na znaky morseovky
string[] znaky = s.Split(' ');
// iterace znaků morseovky
foreach (string morseuvZnak in znaky)
{
char abecedniZnak = '?';
int index = Array.IndexOf(morseovyZnaky, morseuvZnak);
if (index >= 0) // znak nalezen
abecedniZnak = abecedniZnaky[index];
zprava += abecedniZnak;
}
Console.WriteLine("Dekódovaná zpráva: {0}", zprava);
Console.ReadKey();
{/CSHARP_CONSOLE}
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, kdy uživatel zadá
např. více mezer mezi znaky (to uživatelé dělají rádi).
Split()
poté vytvoří o jeden řetězec v poli navíc, 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! Nyní máte za úkol naprogramovat si program opačný, který naopak
zakóduje řetězec do morseovky. Kód bude velmi podobný. Se
Split()
a Join()
se během C# kurzu potkáme 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:
Pojďme si to vyzkoušet:
{CSHARP_CONSOLE}
Console.WriteLine("První řádka\nDruhá řádka");
Console.ReadKey();
{/CSHARP_CONSOLE}
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é
\
. V tom případě ho musíme tzv. odescapovat:
{CSHARP_CONSOLE}
Console.WriteLine("Toto je zpětné lomítko: \\");
{/CSHARP_CONSOLE}
Stejným způsobem můžeme odescapovat např. uvozovku tak, aby ji C# nechápal jako konec řetězce:
{CSHARP_CONSOLE}
Console.WriteLine("Toto je uvozovka: \"");
{/CSHARP_CONSOLE}
Problém může nastat, 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, zavináč na klávesnici napíšeme pomocí Pravého Alt + V:
Díky tomuto modifikátoru C# automaticky escapuje celý námi napsaný řetězec v kódu:
{CSHARP_CONSOLE}
Console.WriteLine(@"C:\Users\sdraco\Dropbox\itnetwork");
{/CSHARP_CONSOLE}
Vstupy z konzole a polí v okenních aplikacích se samozřejmě escapují
samy, 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 kvízu, Kvíz - Textové řetězce v C# .NET, si vyzkouší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 1489x (246.43 kB)
Aplikace je včetně zdrojových kódů v jazyce C#