Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 13 - Textové řetězce v Javě podruhé - Práce s jednotlivými znaky

V minulé lekci, Nejčastější chyby Java nováčků - Umíš pojmenovat proměnné?, jsme si ukázali nejčastější chyby začátečníků v Javě ohledně pojmenování proměnných.

V dnešním Java tutoriálu se budeme zabývat přístupem k jednotlivým znakům textového řetězce.

Textový řetězec

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. Pro přístup k jednotlivým znakům slouží metoda charAt(index), kde index udává index znaku v řetězci (počínaje 0). Opačně pro zjištění indexu zadaného znaku slouží metoda indexOf(znak), kde znak je hledaný znak. Tato metoda vrací index prvního výskytu daného znaku a pokud jej v řetězci nenajde vrátí hodnotu -1.

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

String jazyk = "Java";
System.out.println(jazyk);
System.out.println(jazyk.charAt(2));

Výstup:

Konzolová aplikace
Java
v

A nyní se podíváme na zjištění indexu zadaného znaku:

String jazyk = "Java";
System.out.println(jazyk);
System.out.println(jazyk.indexOf('v'));

Výstup:

Konzolová aplikace
Java
2

Znaky na dané pozici jsou v Javě read-only, nemůžeme je tedy jednoduše změnit.

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 metodou scanner.nextLine(). Ř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 ostatních znaků 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:

// řetězec, který chceme analyzovat
String hora = "Mount Everest";
System.out.println(hora);
hora = hora.toLowerCase();

// 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
for (char znak : hora.toCharArray()) {

}

Protože se jedná o složitější kód, nebudeme zapomínat na komentáře.

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ý typ String. Hlavní cyklus nám projede jednotlivé znaky v řetězci hora. Abychom mohli znaky iterovat (procházet cyklem), musíme si typ String převést na pole znaků. V úvodu jsem říkal, že typ String vlastně pole znaků je, ale ne plnohodnotné. Obsahuje něco navíc a něco mu chybí, např. možnost prvky iterovat cyklem. V cyklu tedy na proměnnou hora zavoláme metodu toCharArray(), která vrátí plnohodnotné pole znaků z řetězce hora. V každé iteraci cyklu bude v proměnné znak aktuální znak řetězce hora.

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

// hlavní cyklus
for (char znak : hora.toCharArray()) {
    if (samohlasky.contains(String.valueOf(znak))) {
        pocetSamohlasek++;
    } else if (souhlasky.contains(String.valueOf(znak))) {
        pocetSouhlasek++;
    }
}

Metodu contains() na řetězci již známe, jako parametr ji lze předat podřetězec. Bohužel nemůžeme předat znak char, musíme tedy znak převést na String. K tomu slouží výše uvedená metoda valueOf(). Daný znak c naší věty tedy nejprve zkusíme vyhledat v řetězce 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. V textu použijeme speciální sekvenci znaků %n (nebo \n), ta způsobí odřádkování. Použitím sekvence %n (namísto \n) zajistíme cross-platform kompatibilitu. Java tedy odřádkuje správně jak na MacOS, tak třeba na Windows:

System.out.printf("Samohlásek: %d%n", pocetSamohlasek);
System.out.printf("Souhlásek: %d%n", pocetSouhlasek);
System.out.printf("Nepísmenných znaků: %d%n", hora.length() - (pocetSamohlasek + pocetSouhlasek));

Výstup programu:

Konzolová aplikace
Mount Everest
Samohlásek: 5
Souhlásek: 7
Nepísmenných znaků: 1

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 také 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. Nicméně v Javě máme stále 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 znak a, na pozici 98 znak 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 znak; // znak
int hodnotaAscii; // ordinální (ASCII) hodnota znaku
// převedeme znak na jeho ASCII hodnotu
znak = 'a';
hodnotaAscii = (int)znak;
System.out.printf("Znak %c jsme převedli na ASCII hodnotu %d%n", znak, hodnotaAscii);
// Převedeme ASCII hodnotu na znak
hodnotaAscii = 98;
znak = (char)hodnotaAscii;
System.out.printf("ASCII hodnotu %d jsme převedli na znak %c", hodnotaAscii, znak);

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

Caesarova šifra

Vytvoříme si jednoduchý program pro šifrování textu. Pokud jste někdy slyšeli o Caesarově š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 Caesarova šifra. Program si dokonce můžete vyzkoušet v praxi - Online caesarova š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 scanner.nextLine(). Šifra nepočítá s diakritikou, mezerami a interpunkčními znaménky. Diakritiku budeme bojkotovat 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 puvodniZprava = "gaiusjuliuscaesar";
System.out.printf("Původní zpráva: %s%n", puvodniZprava);
String zasifrovanaZprava = "";
int posun = 1;

// cyklus projíždějící jednotlivé znaky
for (char znak : puvodniZprava.toCharArray()) {

}

// výpis
System.out.printf("Zašifrovaná zpráva: %s%n", zasifrovanaZprava);

Nyní se přesuneme dovnitř cyklu, převedeme proměnnou se znakem znak 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 ascii = (int)znak;
ascii += posun;
znak = (char)ascii;
zasifrovanaZprava += znak;

Výstup programu:

Konzolová aplikace
Původní zpráva: gaiusjuliuscaesar
Zašifrovaná zpráva: hbjvtkvmjvtdbftbs

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 znaku z přešel opět ke znaku 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 ascii = (int)znak;
ascii += posun;
// kontrola přetečení
if (ascii > (int)'z') {
    ascii -= 26;
}
znak = (char)ascii;
zprava += znak;

Pokud hodnota přesáhne ASCII hodnotu 'z', snížíme ji o 26 znaků (tolik znaků má anglická abeceda). Operátor -= vykoná to samé, jako bychom napsali hodnota = hodnota - 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í :)

V následujícím cvičení, Řešené úlohy k 13. lekci Javy, si procvičí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 1208x (19.24 kB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

Předchozí článek
Nejčastější chyby Java nováčků - Umíš pojmenovat proměnné?
Všechny články v sekci
Základní konstrukce jazyka Java
Přeskočit článek
(nedoporučujeme)
Řešené úlohy k 13. lekci Javy
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
610 hlasů
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 se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity