9. díl - Textové řetězce v C# podruhé - práce s jednotlivými znaky

C# .NET Základní konstrukce Textové řetězce v C# podruhé - práce s jednotlivými znaky American English version English version

V milém dílu seriálu o C# jsme se naučili pracovat s polem. Pokud jste vycítili nějakou podobnost mezi polem a textovým řetězcem, tak jste vycítili správně. Pro ostatní může být překvapením, že string je v podstatě pole znaků (charů) a můžeme s ním i takto pracovat.

Nejprve si vyzkoušejme, že to všechno funguje. Rozcvičíme se na jednoduchém vypsání znaku na dané pozici:

string s = "Hello devbook";
Console.WriteLine(s);
Console.WriteLine(s[2]);
Console.ReadKey();

Výstup:

Hello devbook
l

Vidíme, že můžeme ke znakům v řetězci přistupovat přes hranatou závorku, jako tomu je i u pole. Zklamáním může být, že znaky na dané pozici jsou v C# read-only, nemůžeme tedy napsat:

string s = "Hello devbook";
s[1] = "o";
Console.WriteLine(s);
Console.ReadKey();

Samozřejmě to jde udělat jinak, později si to ukážeme, zatím se budeme věnovat pouze čtení jednotlivých znaků.

Analýza výskytu znaků ve větě

Napišme si jednoduchý program, který nám analyzuje zadanou větu. Bude nás zajímat počet samohlásek, souhlásek a počet nepísmenných znaků (např. mezera nebo !).

Daný textový řetězec si nejprve v programu zadáme napevno, abychom ho nemuseli při každém spuštění psát. Až bude program hotový, nahradíme ho Console.ReadLine(). Řetězec budeme projíždět cyklem po jednom znaku. Rovnou zde říkám, že neapelujeme na rychlost programu a budeme volit názorná a jednoduchá řešení.

Nejprve si připravme kód, definujme si samohlásky a souhlásky. Počet nepísmen nemusíme počítat, bude to délka řetězce mínus samohlásky a souhlásky. Abychom nemuseli řešit velikost písmen, celý řetězec na začátku převedeme na malá písmena. Připravme si proměnné, do kterých budeme ukládat jednotlivé počty. Protože se jedná o složitější kód, nebudeme zapomínat na komentáře.

pozn.: Kdybyste věděli, jak se správně říká nepísmennému znaku, napište mi to prosím do komentáře pod článek :)

// řetězec, který chceme analyzovat
string s = "Programátor se zasekne ve sprše, protože instrukce na šampónu byly: Namydlit, omýt, opakovat.";
Console.WriteLine(s);
s = s.ToLower();

// inicializace počítadel
int pocetSamohlasek = 0;
int pocetSouhlasek = 0;

// definice typů znaků
string samohlasky = "aeiouyáéěíóúůý";
string souhlasky = "bcčdďfghjklmnpqrřsštťvwxzž";

// hlavní cyklus
foreach (char c in s)
{

}

Console.ReadKey();

Zpočátku si připravíme řetězec a převedeme ho na malá písmena. Počítadla vynulujeme. Na definice znaků nám postačí obyčejné stringy. Hlavní cyklus nám projede jednotlivé znaky v řetězci s, přičemž v každé iteraci cyklu bude v proměnné c aktuální znak.

Pojďme plnit počítadla, pro jednoduchost již nebudu opisovat zbytek kódu a přesunu se jen k cyklu:

// hlavní cyklus
foreach (char c in s)
{
        if (samohlasky.Contains(c))
                pocetSamohlasek++;
        else
        if (souhlasky.Contains(c))
                pocetSouhlasek++;
}

Metodu Contains() na řetězci již známe, jako parametr ji lze předat jak podřetězec, tak přímo znak. Daný znak c naší věty tedy nejprve zkusíme vyhledat v řetězci samohlasky a případně zvýšit jejich počítadlo. Pokud v samohláskách není, podíváme se do souhlásek a případně opětovně zvýšíme jejich počítadlo.

Nyní nám chybí již jen výpis na konec:

Console.WriteLine("Samohlásek: {0}", pocetSamohlasek);
Console.WriteLine("Souhlásek: {0}", pocetSouhlasek);
Console.WriteLine("Nepísmenných znaků: {0}", s.Length - (pocetSamohlasek + pocetSouhlasek));
Analyzátor samohlásek, souhlásek a znaků ve větě v C# .NET

A je to!

ASCII hodnota

Možná jste již někdy slyšeli o ASCII tabulce. Zejména v éře operačního systému MS-DOS prakticky nebyla jiná možnost, jak zaznamenávat text. Jednotlivé znaky byly uloženy jako čísla typu byte, tedy s rozsahem hodnot od 0 do 255. V systému byla uložena tzv. ASCII tabulka, která měla 256 znaků a každému ASCII kódu (číselnému kódu) přiřazovala jeden znak.

Asi je vám jasné, proč tento způsob nepřetrval dodnes. Do tabulky se jednoduše nevešly všechny znaky všech národních abeced, nyní se používá Unicode (UTF-8) kódování, kde jsou znaky reprezentovány trochu jiným způsobem. V C# máme možnost pracovat s ASCII hodnotami jednotlivých znaků. Hlavní výhoda je v tom, že znaky jsou uloženy v tabulce za sebou, podle abecedy. Např. na pozici 97 nalezneme "a", 98 "b" a podobně. Podobně je to s čísly, diakritické znaky tam budou bohužel jen nějak rozházeny.

Zkusme si nyní převést znak do jeho ASCII hodnoty a naopak podle ASCII hodnoty daný znak vytvořit:

char c; // znak
int i; // ordinální (ASCII) hodnota znaku
// převedeme znak na jeho ASCII hodnotu
c = 'a';
i = (int)c;
Console.WriteLine("Znak {0} jsme převedli na ASCII hodnotu {1}", c, i);
// Převedeme ASCII hodnotu na znak
i = 98;
c = (char)i;
Console.WriteLine("ASCII hodnotu {1} jsme převedli na znak {0}", c, i);
Console.ReadKey();

Převodům se říká přetypování, ale o tom se blíže pobavíme až později.

Cézarova šifra

Vytvoříme si jednoduchý program pro šifrování textu. Pokud jste někdy slyšeli o Cézarově šifře, bude to přesně to, co si zde naprogramujeme. Šifrování textu spočívá v posouvání znaku v abecedě o určitý, pevně stanovený počet znaků. Například slovo "ahoj" se s posunem textu o 1 přeloží jako "bipk". Posun umožníme uživateli vybrat. Algoritmus zde máme samozřejmě opět vysvětlený a to v článku Cézarova šifra . Program si dokonce můžete vyzkoušet v praxi - Online cézarova šifra.

Vraťme se k programování a připravme si kód. Budeme potřebovat proměnné pro původní text, zašifrovanou zprávu a pro posun. Dále cyklus projíždějící jednotlivé znaky a výpis zašifrované zprávy. Zprávu si necháme zapsanou napevno v kódu, abychom ji nemuseli při každém spuštění programu psát. Po dokončení nahradíme obsah proměnné metodou Console.ReadLine(). Šifra nepočítá s diakritikou, mezerami a interpunkčními znaménky. Diakritiku budeme bojkovat a budeme předpokládat, že ji uživatel nebude zadávat. Ideálně bychom poté měli diakritiku před šifrováním odstranit, stejně tak cokoli kromě písmen.

// inicializace proměnných
string s = "cernediryjsoutamkdebuhdelilnulou";
Console.WriteLine("Původní zpráva: {0}", s);
string zprava = "";
int posun = 1;

// cyklus projíždějící jednotlivé znaky
foreach(char c in s)
{

}

// výpis
Console.WriteLine("Zašifrovaná zpráva: {0}", zprava);
Console.ReadKey();

Nyní se přesuneme dovnitř cyklu, převedeme znak c na ASCII hodnotu (neboli ordinální hodnotu), tuto hodnotu zvýšíme o posun a převedeme zpět na znak. Tento znak nakonec připojíme k výsledné zprávě:

int i = (int)c;
i += posun;
char znak = (char)i;
zprava += znak;
Cézarova šifra v C#

Program si vyzkoušíme. Výsledek vypadá docela dobře. Zkusme si však zadat vyšší posun nebo napsat slovo "zebra". Vidíme, že znaky mohou po "z" přetéct do ASCII hodnot dalších znaků, v textu tedy již nemáme jen písmena, ale další ošklivé znaky. Uzavřeme znaky do kruhu tak, aby posun plynule po "z" přešel opět k "a" a dále. Postačí nám k tomu jednoduchá podmínka, která od nové ASCII hodnoty odečte celou abecedu tak, abychom začínali opět na "a".

int i = (int)c;
i += posun;
// kontrola přetečení
if (i > (int)'z')
        i -= 26;
char znak = (char)i;
zprava += znak;

Pokud i přesáhne ASCII hodnotu 'z', snížíme ho o 26 znaků (tolik znaků má anglická abeceda). Operátor -= vykoná to samé, jako bychom napsali i = i - 26. Je to jednoduché a náš program je nyní funkční. Všimněme si, že nikde nepoužíváme přímé kódy znaků, v podmínce je (int)'z', i když bychom tam mohli napsat rovnou 122. Je to z důvodu, aby byl náš program plně odstíněn od explicitních ASCII hodnot a bylo lépe viditelné, jak funguje. Cvičně si zkuste udělat dešifrování.

Příště si ukážeme, že string umí přeci jen ještě něco navíc. Prozradím, že budeme dekódovat Morzeovu abecedu.


 

Stáhnout

Staženo 983x (48.75 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 7.-8. lekci C# .NET
Miniatura
Všechny články v sekci
Základní konstrukce jazyka C#
Miniatura
Následující článek
Cvičení k 9. lekci C# .NET

 

 

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

Avatar
Milan Křepelka
Redaktor
Avatar
Milan Křepelka:
Console.WriteLine("Zadej heslo:");
string h = "sims";
string s = Console.ReadLine();
if (h.Equals(s))
    Console.WriteLine("Vítej");
else
    Console.WriteLine("Zadané heslo je špatně");
Console.ReadLine();
 
Odpovědět  +1 17.8.2015 13:41
Avatar
krepsy3
Redaktor
Avatar
krepsy3:

Když jsi při šifrování prováděl:
if (c == 'z')
c = (char((int)c - 26)

Tak musíš při dešifrování udělat pravý opak:
if (c == 'a')
c = (char)((int)c + 26)

Protože tam, kde se při šifrování ošetřovalo "maximum" písmeno z,
tam se nyní ošetřuje "minimum" písmeno a ;)

Odpovědět  +1 5.10.2015 17:39
Programátor je stroj k převodu kávy na kód.
Avatar
krepsy3
Redaktor
Avatar
Odpovídá na krepsy3
krepsy3:

Kecám, promiň: ;)
šifrace:
if (c > (int)'z')
c = (char((int)c - 26)

dešifrace:
if (c < (int)'a')
c = (char((int)c + 26)

Odpovědět 5.10.2015 17:45
Programátor je stroj k převodu kávy na kód.
Avatar
rimodar65
Člen
Avatar
rimodar65:

Nějakmi nejde drobná úprava
>
Analýza výskytu znaků ve větě
>

Console.WriteLine("Zadej slova a já ti spočítám samohlásky, souhlásky a mezery.Zadávání ukonči ENTEREM");
           String s = Console.ReadLine();
            int a = int.Parse(s);
            Console.WriteLine(s);//TOHLE MI NEJDE
            while (a > 0)
            {

           s = s.ToLower();// převod na malá písmena
           // inicializace počítadel
           int pocetSamohlasek = 0;
           int pocetSouhlasek = 0;

           // definice typů znaků
           string samohlasky = "aeiouyáéěíóúůý";
           string souhlasky = "bcčdďfghjklmnpqrřsštťvwxzž";

           // hlavní cyklus
           foreach (char c in s)
           {
               if (samohlasky.Contains(c))//contains prohledá c jestli se v ní nachází samohlásky
                   pocetSamohlasek++;
               else
                   if (souhlasky.Contains(c))
                       pocetSouhlasek++;
           }
           Console.WriteLine("Samohlásek: {0}", pocetSamohlasek);
           Console.WriteLine("Souhlásek: {0}", pocetSouhlasek);
           Console.WriteLine("Nepísmenných znaků: {0}", s.Length - (pocetSamohlasek + pocetSouhlasek));
           Console.WriteLine("Znaků je celkem:{0}", pocetSamohlasek + pocetSouhlasek);

           Console.ReadKey();
 
Odpovědět 30. ledna 22:50
Avatar
rimodar65
Člen
Avatar
rimodar65:

Po přečtení dřívějších dílů asi chápu chybu,tak na to jdu jinak.....

 
Odpovědět 30. ledna 23:04
Avatar
Dominik Gavrecký:

Nech sa na to pozriem ako chcem nerozumiem prečo práve "char c". Rozumiem že sme určili dátoví typ na char premene c ale čo ta premenná obsahuje ? Je to nejaká špeciálna metoda ktorá rozoberie string na jednotlivé hodnoti pola a už ho len prechádza práve cez to in ? Mohol by mi to niekto logický popísať ? Ďakujem

// hlavní cyklus
foreach (char c in s)
{

}
Odpovědět 10. dubna 23:01
Hlupák nie je ten kto niečo nevie, hlupákom sa stávaš v momente keď sa na to bojíš opýtať.
Avatar
Petr Stastny
Redaktor
Avatar
Petr Stastny:

Ahoj, to počitadlo souhlásek, samohlásek atd by šlo udělat jednodušeji:

string s = "Programátor se zasekne ve sprše, protože instrukce na šampónu byly: Namydlit, omýt, opakovat.";

            s = s.ToLower();

            int a = 0;
            int b = 0;
            int c = 0;

            string samo = "aeiouyáéěíóúůý";
            string sou = "bcčdďfghjklmnpqrřsštťvwxzž";


            foreach (char h in s)
            {
                if (samo.Contains(h))
                    a++;
                else if (sou.Contains(h))
                    b++;
                else
                    c++;

            }
            Console.WriteLine("Samohlasek: {0} | Souhlasek: {1} | Ostatni znaky: {2}", a, b, c);
            Console.ReadKey();
 
Odpovědět  +2 18. dubna 10:50
Avatar
Odpovídá na Dominik Gavrecký
Dávid Šutor:
// hlavní cyklus
foreach (char c in s)
{

}

Tento cyklus funguje následovne. Keď cyklus začne, premenná c nadobudne hodnotu prvého znaku (char) v stringu s. Po vykonaní príkazov v tele cyklu premenná c nadobudne hodnotu ďalšieho znaku (char). Takto to bude pokračovať do vtedy, kým neprejde všetky znaky (char). Premenná c bude iba nadobúdať char.

 
Odpovědět 14. května 20:49
Avatar
Petr Stastny
Redaktor
Avatar
Petr Stastny:

A ohledně ASCII, tohle je trochu delší, ale dobře to ukazuje, jak jde s ASCII pracovat :)

http://www.itnetwork.cz/dev-lighter/732

 
Odpovědět 14. května 21:20
Avatar
Robert
Člen
Avatar
Robert:

//3. Cézarova šifra:
Console.Write­Line("Zadaj ľubovoľný reťazec a ja ho zašifrujem.");
Console.Write­Line("POZOR!!! Zapisuj bez diakritiky a bez medzier!");
Console.Write("Tex­t: ");

string sprava = Console.ReadLine();

//Opýtať sa na hodnotu posunu
Console.Write("O akú hodnotu chceš posúvať text? [napr. o 2 znaky...]:");
int posun = int.Parse(Con­sole.ReadLine());
Console.Write­Line();
Console.Write­Line("Povodná správa: {0}", sprava);
sprava = sprava.ToLower();

//dekloarovanie premennej pre uloženie zašifrovanej správy
string sifrovana = "";

foreach(char z in sprava){

int j = (int)z;
j += posun;

//kontrola pretečenia!!!
if(j > (int)'z'){
j -= 26;
}

char znak = (char)j;
sifrovana += znak;
}
Console.Write­Line("Zašifro­vaná správa: {0}", sifrovana);

string desifrovana = "";

//Dešifrovanie správy
Console.Write­Line("Dešifro­vanie zašifrovanej správy: {0}", sifrovana);
foreach(char z in sifrovana){
int j = (int)z;
j -= posun;

//Kontrola pretečenia
if(j<(int)'a'){
j += 26;
}

char znak = (char)j;
desifrovana += znak;

}

Console.Write­Line("Dešifro­vaná správa: {0}", desifrovana);

 
Odpovědět 20. října 22:26
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 53. Zobrazit vše