Mikuláš je tu! Získaj 90 % extra kreditov ZADARMO s promo kódom CERTIK90 pri nákupe od 1 199 kreditov. Len do nedele 7. 12. 2025! Zisti viac:
NOVINKA: Najžiadanejšie rekvalifikačné kurzy teraz s 50% zľavou + kurz AI ZADARMO. Nečakaj, táto ponuka dlho nevydrží! Zisti viac:

11. diel - Textové reťazce v C# druhýkrát - Práca s jednotlivými znakmi

V predchádzajúcej lekcii, Najčastejšie chyby C# nováčikov - Vieš pomenovať premenné?, sme si ukázali najčastejšie chyby začiatočníkov v C# .NET týkajúce sa pomenovania premenných.

Ak patríte medzi tých, ktorí v C# videli nejakú podobnosť medzi poľom a textovým reťazcom, tak ste uvažovali správne. Pre tých ostatných môže byť prekvapením, že string je v podstate pole znakov (hodnôt typu char) a môžeme s ním pracovať aj takto.

Najprv si vyskúšajme, či to všetko funguje. Rozcvičíme sa na jednoduchom vypísaní znaku na danej pozícii:

string s = "C# .NET";
Console.WriteLine(s);
Console.WriteLine(s[1]);
Console.ReadKey();

Výstup:

Konzolová aplikácia
C# .NET
#

Vidíme, že k znakom v reťazci môžeme pristupovať cez hranatú zátvorku, ako je to aj pri poli. Sklamaním môže byť, že znaky sú na danej pozícii v C# read-only, nemôžeme teda napísať:

// this code doesn't work
string s = "C# .NET";
s[1] = '!';
Console.WriteLine(s);
Console.ReadKey();

Samozrejme to možno urobiť inak, neskôr si to ukážeme, zatiaľ sa však budeme venovať iba čítaniu jednotlivých znakov.

Analýza výskytu znakov vo vete

Napíšme si jednoduchý program, ktorý nám analyzuje zadanú vetu. Bude nás zaujímať počet samohlások, spoluhlások a počet nepísmenkových znakov (napr. medzera alebo !).

Daný textový reťazec si najskôr v programe zadáme napevno, aby sme ho nemuseli pri každom spustení písať. Keď bude program hotový, nahradíme ho Console.ReadLine(). Reťazec budeme prechádzať cyklom po jednom znaku. Je nutné podotknúť, že neapelujeme na rýchlosť programu a budeme voliť názorné a jednoduché riešenia.

Najprv si pripravme kód, definujme si samohlásky a spoluhlásky. Počet ostatných znakov nemusíme počítať, pretože bude zodpovedať dĺžke reťazca mínus samohlásky a spoluhlásky. Aby sme nemuseli riešiť veľkosť písmen, celý reťazec na začiatku prevedieme na malé písmená. Pripravme si premenné, do ktorých budeme ukladať jednotlivé počty. Pretože sa jedná o zložitejší kód, nezabudneme ho opatriť komentármi:

// the string that we want to analyze
string s = "A programmer gets stuck in the shower because the instructions on the shampoo were: Lather, Wash, and Repeat.";
Console.WriteLine(s);
s = s.ToLower();

// counters initialization
int vowelsCount = 0;
int consonantsCount = 0;

// definition of character groups
string vowels = "aeiouy";
string consonants = "bcdfghjklmnpqrstvwxz";

// the main loop
foreach (char c in s)
{

}

Console.ReadKey();

Spočiatku si pripravíme reťazec a prevedieme ho na malé písmená. Počítadlá vynulujeme. Na definície znakov nám budú stačiť obyčajné reťazce. Hlavný cyklus nám prejde jednotlivé znaky v reťazci s, pričom v každej iterácii cyklu bude v premennej c aktuálny znak.

Poďme vyplniť počítadlá. Pre jednoduchosť už nebudeme opisovať zvyšok kódu a presunieme sa len k cyklu:

// the main loop
foreach (char c in s)
{
    if (vowels.Contains(c))
        vowelsCount++;
    else
    if (consonants.Contains(c))
        consonantsCount++;
}

Metódu Contains() na reťazci už poznáme. Ako parameter jej možno odovzdať podreťazec, ale aj priamo znak. Daný znak c našej vety teda najskôr skúsime vyhľadať v reťazci vowels a prípadne zvýšiť ich počítadlo. Ak znak v samohláskach nie je, pozrieme sa do spoluhlások a prípadne opätovne zvýšime ich počítadlo.

Teraz nám už chýba len výpis na koniec:

Console.WriteLine("Vowels: {0}", vowelsCount);
Console.WriteLine("Consonants: {0}", consonantsCount);
Console.WriteLine("Non-alphanumeric characters: {0}", s.Length - (vowelsCount + consonantsCount));

Výstup:

Konzolová aplikácia
A programmer gets stuck in the shower because the instructions on the shampoo were: Lather, Wash, and Repeat.
Vowels: 33
Consonants: 55
Non-alphanumeric characters: 21

A je to!

ASCII hodnota

Možno ste už niekedy počuli o tabuľke ASCII. Najmä v ére operačného systému MS-DOS prakticky neexistovala iná možnosť, ako zaznamenávať text. Jednotlivé znaky boli uložené ako čísla typu byte, teda s rozsahom hodnôt od 0 do 255. V systéme bola uložená tzv. ASCII tabuľka, ktorá mala 256 znakov a každému ASCII kódu (číselnému kódu) priraďovala jeden znak.

Asi je vám jasné, prečo tento spôsob už dnes nefunguje. Do tabuľky sa jednoducho nezmestili všetky znaky všetkých národných abecied. Dnes sa používa Unicode (UTF-8) kódovanie, v ktorom sú znaky reprezentované trochu iným spôsobom. V C# máme možnosť pracovať s ASCII hodnotami jednotlivých znakov. Hlavná výhoda spočíva v tom, že znaky sú uložené v tabuľke za sebou podľa abecedy. Napr. na pozícii 97 teda nájdeme 'a', na 98 'b' a tak ďalej. Podobne je to s číslami, avšak diakritické znaky sú v ASCII bohužiaľ len nejako rozhádzané.

Skúsme si teraz previesť znak do jeho ASCII hodnoty a naopak podľa ASCII hodnoty daný znak vytvoriť:

char c; // character
int i; // ordinal (ASCII) value of the character
// conversion from text to ASCII value
c = 'a';
i = (int)c;
Console.WriteLine("The character {0} was converted to its ASCII value of {1}", c, i);
// conversion from an ASCII value to text
i = 98;
c = (char)i;
Console.WriteLine("The ASCII value of {1} was converted to its textual value of {0}", c, i);
Console.ReadKey();

Prevodom sa hovorí pretypovanie, ale o tom si bližšie povieme až neskôr.

Caesarova šifra

Vytvoríme si jednoduchý program na šifrovanie textu. Ak ste niekedy počuli o Caesarovej šifre, bude to presne to, čo si tu naprogramujeme. Šifrovanie textu spočíva v posúvaní znaku v abecede o určitý, pevne stanovený počet znakov. Napríklad slovo "hello" sa s posunom textu o 1 zašifruje ako "ifmmp". Posun umožníme užívateľovi vybrať. Algoritmus tu máme samozrejme opäť vysvetlený, a to v článku Caesarova šifra. Program si dokonca môžete vyskúšať v praxi - Online Caesarova šifra.

Vráťme sa k programovaniu a pripravme si kód. Budeme potrebovať premenné pre pôvodný text, zašifrovanú správu a pre posun. Ďalej cyklus prechádzajúci jednotlivé znaky a výpis zašifrovanej správy. Správu si necháme zapísanú napevno v kóde, aby sme ju nemuseli pri každom spustení programu písať. Po dokončení nahradíme obsah premennej metódou Console.ReadLine(). Šifra nepočíta s diakritikou, medzerami a interpunkčnými znamienkami. Diakritiku budeme ignorovať a budeme predpokladať, že ju používateľ nebude zadávať. Diakritiku, rovnako ako čokoľvek okrem písmen, by sme potom mali v ideálnom prípade pred šifrovaním odstrániť:

// variable initialization
string s = "gaiusjuliuscaesar";
Console.WriteLine("Original message: {0}", s);
string message = "";
int shift = 1;

// loop iterating over characters
foreach(char c in s)
{

}

// printing
Console.WriteLine("Encrypted message: {0}", message);
Console.ReadKey();

Kódom string message = "" ukladáme do premennej message prázdny string.

Teraz sa presunieme dovnútra cyklu, prevedieme znak v c na ASCII hodnotu (čiže ordinálnu hodnotu), túto hodnotu zvýšime o shift a prevedieme späť na znak. Tento znak nakoniec pripojíme k výslednej správe:

int i = (int)c;
i += shift;
char character = (char)i;
message += character;

Operátor += vykoná to isté, ako keby sme napísali i = i + shift.

Výstup:

Konzolová aplikácia
Original message: gaiusjuliuscaesar
Encrypted message: hbjvtkvmjvtdbftbs

Program si vyskúšame. Výsledok vyzerá celkom dobre. Skúsme si však zadať vyšší posun alebo napísať slovo „zebra“. Vidíme, že znaky môžu po 'z' pretiecť do ASCII hodnôt ďalších znakov, v texte teda už nemáme len písmená, ale ďalšie škaredé znaky. Uzavrieme znaky do kruhu tak, aby posun plynule po 'z' prešiel opäť k 'a' a ďalej. Na to nám bude stačiť jednoduchá podmienka, ktorá od novej ASCII hodnoty odpočíta celú abecedu tak, aby sme začínali opäť na 'a':

int i = (int)c;
i += shift;
// overflow control
if (i > (int)'z')
    i -= 26;
char character = (char)i;
message += character;

Pokiaľ i presiahne ASCII hodnotu 'z', znížime ho o 26 znakov (toľko znakov má anglická abeceda). Operátor -= vykoná to isté, ako keby sme napísali i = i - 26. Je to jednoduché riešenie a náš program je teraz funkčný. Všimnime si, že nikde nepoužívame priame kódy znakov. V podmienke je teda (int)'z', aj keď by sme tam mohli napísať rovno 122. Je to z dôvodu, aby bol náš program odbremenený od explicitných ASCII hodnôt a bolo viditeľnejšie, ako funguje. Skúsme si potom cvične vytvoriť dešifrovanie.

V nasledujúcom cvičení, Riešené úlohy k 11. lekcii C# .NET, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 12x (97.12 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Predchádzajúci článok
Najčastejšie chyby C# nováčikov - Vieš pomenovať premenné?
Všetky články v sekcii
Základná konštrukcia jazyka C# .NET
Preskočiť článok
(neodporúčame)
Riešené úlohy k 11. lekcii C# .NET
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
29 hlasov
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity