IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

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

V minulej 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 ohľadom pomenovania premenných.

Pokiaľ 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 aj takto pracovať.

Najprv si vyskúšajme, že 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 tomu je aj pri poli. Sklamaním môže byť, že znaky na danej pozícii sú 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ísmenný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 postačia 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 plniť 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ť ako podreťazec, tak 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. Pokiaľ 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 nepretrval dodnes. 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 sa bližšie pobavíme 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 bojkotovať 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. Postačí nám k tomu 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 plne odtienený od explicitných ASCII hodnôt a bolo viditeľnejšie, ako funguje. Cvične si skúsme vytvoriť dešifrovanie.

V nasledujúcom cvičení, Riešené úlohy k 10. 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é 4x (96.63 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 10. lekcii C# .NET
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
2 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