10. díl - Textové řetězce v C# do třetice - Split a Join

C# .NET Základní konstrukce Textové řetězce v C# do třetice - Split a Join American English version English version

Minule jsme si ukázali, že string je vlastně pole znaků. 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("Já bych všechny ty internety zakázala.".Insert(29, "ne"));

Výstup:

Já bych všechny ty internety nezakázala.

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("Kdo se směje naposled, ten je admin.".Remove(13, 8));

Výstup:

Kdo se směje, ten je admin.

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("Kdo se směje naposled, ten je admin.".Substring(13, 8));

Výstup:

naposled

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("akát".CompareTo("blýskavice"));

Výstup:

-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, řádek středník

Na stringu můžeme volat metodu Split(), která bere jako parametr separátor (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. 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 znaků morzeovky
foreach (string morseuvZnak in znaky)
{

}

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ý. Ten bychom měli poté v cyklu detekovat a ignorovat, my se s tím v tutoriálu nebudeme zabývat.

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.

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

Console.WriteLine("Dekódovaná zpráva: {0}", zprava);
Console.ReadKey();
Program dekodér Morseovy abecedy v C#

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 seriálu 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.

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 @, díky kterému C# automaticky escapuje celý námi napsaný řetězec v kódu:

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

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.

Tímto jsme v podstatě zakončili sekci se základní strukturou jazyka C#, příště si uvedeme bonusový díl o vícerozměrných polích a sekci ještě zakončuje ještě něco o matematické třídě a pokročilém ovládání konzole. Ze základních konstrukcí jazyka vás tu ale již nic nepřekvapí :) V podstatě byste již klidně mohli jít i na objekty, doporučují ale zbylé články ještě alespoň projet, jedná se přeci jen stále o základní znalosti, které byste měli mít.


 

Stáhnout

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

 

  Aktivity (3)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (16 hlasů) :
4.93754.93754.93754.93754.9375


 


Miniatura
Předchozí článek
Cvičení k 9. lekci C# .NET
Miniatura
Všechny články v sekci
Základní konstrukce jazyka C#
Miniatura
Následující článek
Cvičení k 10. lekci C# .NET

 

 

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

Avatar
fejjaaas
Člen
Avatar
fejjaaas:

U příkladu na metodu Remove není výstupem: Kdo se směje, ten je admin. Správný výstup je: Kdo se směje ten je admin. :) To jen tak, kdyby to někoho mátlo.

 
Odpovědět  +1 22.2.2014 12:46
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovědět 21.3.2014 17:22
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
fatst
Člen
Avatar
fatst:

Prosím o vysvětlení. Mám následující kód, který tu je a funguje, ale stále nechápu použití IndexOf():

            // převedu na malá odstraním mezery a zavolám trim ten zařídí aby tam nebyly nějaké white-space znaky
            vstup = vstup.ToLower().Replace(" ", "").Trim();
            // převod na pole
            char[] poleZnakuVstupu = vstup.ToCharArray();
            foreach (char pismeno in poleZnakuVstupu)
            {
                   string abecedniZnak = "?";
                   int index = abecedniZnaky.IndexOf(pismeno);
                   if (index >= 0) // znak nalezen
                           abecedniZnak = morseovyZnaky[index] + " "; // Vypisuji s mezerou, pro lepší čitelnost
                   vystup += abecedniZnak;
            }
            Console.WriteLine("Dekódovaná zpráva: {0}", vystup);
        // řetězec, který chceme kódovat
        string s = vystup;
        Console.WriteLine ();
        Console.WriteLine ("=========== Kódování =============");
        Console.WriteLine ("Původní zpráva:{0}", s);
        // řetězec s kódovanou zprávou
        string zprava = "";
        // rozbití řetězce na znaky morzeovky
        string[] znaky = s.Split(' ');
        // iterace znaků morzeovky
        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);

Prosím tedy o vysvětlení, proč jednou má IndexOf jen jeden parametr

int index = abecedniZnaky.IndexOf(pismeno);

a podruhé je tam Array a dva parametry:

int index = Array.IndexOf(morseovyZnaky, morseuvZnak);

.
Navíc jednou je to za proměnnou a podruhé ne. Nějak se mi z toho motá šiška.
Díky moc.

 
Odpovědět 22.4.2015 19:09
Avatar
fik...
Člen
Avatar
Odpovídá na fatst
fik...:

Jde vlastně o dvě různé metody se stejným názvem. V prvním případě voláš metodu přímo na proměnné a tak už v nemusíš proměnnou psát znovu do parametrů metody. Ve druhém případě jde ale o statickou metodu(volá se na třídě Array a ne na proměnné) a tak tam musíš proměnou napsat jako parametr, ale výsledek bude stejný. Více se dozvíš v tutoriálu o OOP. Snad ti to trochu pomohlo.:)

 
Odpovědět  +1 22.4.2015 19:35
Avatar
fatst
Člen
Avatar
Odpovídá na fik...
fatst:

Díky moc za vysvětlení. Zatím jsem zmaten, ale jestli je to dále, pokračuji. Ještě si to 5x přečtu... :-)

 
Odpovědět 22.4.2015 21:52
Avatar
Shakul
Redaktor
Avatar
Shakul:

Ahoj.
Mám problém s převedením textu do Morseovy abecedy.

Console.WriteLine("Zadej text, který chceš zakódovat do Morseovy abecedy");
string vstup = Console.ReadLine();  //načte vstupní text z konzole
vstup = vstup.ToLower().Trim();  //převede vstupní text na malé písmena a odstraní mezery na začátku a konce řetězce
string zakodovanytext = ""; //vysledny zakódovaný text do morseovky

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

//iterace abecedních znaků
foreach (char abecedniZnak in vstup)
{
    string morseuvZnak = "?";
    int index = Array.IndexOf(abecedniZnaky, abecedniZnak);
    if (index >= 0)  //znak nalezen
        morseuvZnak = morseovyZnaky[index];
    zakodovanytext += morseuvZnak;
}
Console.WriteLine("Dekódovaná zpráva: " + zakodovanytext);
Console.ReadKey();

Program nelze spustit.
Kompilátor říká:
Error 1 The best overloaded method match for 'System.Array­.IndexOf<char>(ch­ar[], char)' has some invalid arguments
Error 2 Argument 1: cannot convert from 'string' to 'char[]'

Prosím o radu. Předem děkuji. :)

Editováno 22.6.2015 0:12
 
Odpovědět 22.6.2015 0:11
Avatar
Odpovídá na Shakul
Ondřej Štorc:
int index = Array.IndexOf(abecedniZnaky.ToCharArray(), abecedniZnak);

Takhle by to mělo fungovat. Ty totiž dáváš do parametru metody string a ono tam má být pole charů

Odpovědět  +1 22.6.2015 8:26
Život je příliš krátký na to, abychom bezpečně odebírali USB z počítače..
Avatar
Migi
Člen
Avatar
Migi:

Ahoj,
zkusil jsem napsat opacny kod, ktere prevede text na morseovku.

string abecedniZnaky = "abcdefghijklmnopqrstuvwxyz";
            string abecedniZnaky1 = "a b c d e f g h i j k l m n o p q r s t u v w x y z";

            string[] abeceda = abecedniZnaky1.Split();

            string[] morseovyZnaky = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....","..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-",
                "...-", ".--", "-..-", "-.--", "--.."};

Console.WriteLine("Zadej text k prekladu:");
            string zprava1 = Console.ReadLine();
            string prekodovana_zprava = "";
            foreach (char c in zprava1)
                if (zprava1[0] == c)
                    prekodovana_zprava = c + " ";
                else

                    prekodovana_zprava = prekodovana_zprava + c + " ";
            prekodovana_zprava = prekodovana_zprava.TrimEnd();

            string[] policko = prekodovana_zprava.Split();

            string dekodovana = "";

            foreach (string pisemko in policko)
            {
                int pozice1 = Array.IndexOf(abeceda, pisemko);
                if (pozice1>=0)
                    if (dekodovana.Length == 0)
                        dekodovana = morseovyZnaky[pozice1];
                    else
                        dekodovana = dekodovana + " " + morseovyZnaky[pozice1];

            }
            Console.WriteLine(dekodovana);
            Console.ReadLine();

Chtel jsem se zeptat, zda me reseni neni zbytecne slozity? Jak funguje to, nejsou osetreny mezery mezi slovy... ale neslo mi cist vstupni text po znacich, ktery pak hledam pomoci Array.IndexOf v poli :-( Znaky abebecedy jsem mel taky jako string pole[] a hledal jsem v tom char a to se bortilo, tak jsem to obesel ze jsi kazdy znak prolozim mezerou a nakonci natrimuju, pak Split a dostanu onez pole pro Array.IndexOf a pak uz to jelo 8-)

Diky

Editováno 7.12.2015 19:08
Odpovědět 7.12.2015 19:06
I ta nejhorsi hodina v zivote trva jen 60 minut...
Avatar
Migi
Člen
Avatar
Odpovídá na Migi
Migi:

No proc to psat jednoduse, kdyz to jde slozite %P Viz vyse. Tohle je to cistejsi reseni, predpokladam.

string abecedniZnaky = "abcdefghijklmnopqrstuvwxyz";
string[] morseovyZnaky = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....","..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-",
                "...-", ".--", "-..-", "-.--", "--.."};
Console.WriteLine("Zadej text k prekladu:");
            string zprava1 = Console.ReadLine();

            string dekodovana = "";

            foreach (char pisemko in zprava1)
            {
                int pozice1 = Array.IndexOf(abecedniZnaky.ToCharArray(), pisemko);
                if (pozice1 >= 0)
                    if (dekodovana.Length == 0)
                        dekodovana = morseovyZnaky[pozice1];
                    else
                        dekodovana = dekodovana + " " + morseovyZnaky[pozice1];

            }
            Console.WriteLine(dekodovana);
            Console.ReadLine();
Odpovědět 7.12.2015 19:13
I ta nejhorsi hodina v zivote trva jen 60 minut...
Avatar
Martin K--a
Člen
Avatar
Martin K--a:

už do toho asi koukám moc dlouho a nevidím kde je ta chyba. :-D

Odpovědět 22. března 10:36
Člověk jako jedinec je inteligentní a racionálně myslící bytost, lidstvo jako celek je stádo nemyslících zvířat
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 41. Zobrazit vše