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

13. diel - Textové reťazce v Jave - Práca s jednotlivými znakmi

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

V dnešnom Java tutoriále sa budeme zaoberať prístupom k jednotlivým znakom textového reťazca.

Textový reťazec

Ak ste vycítili nejakú podobnosť medzi poľom a textovým reťazcom, tak ste vycítili správne. Pre ostatných môže byť prekvapením, že String je v podstate pole znakov (char) a môžeme s ním aj takto pracovať. Pre prístup k jednotlivým znakom slúži metóda charAt(index), kde index udáva index znaku v reťazci (počnúc 0). Opačne na zistenie indexu zadaného znaku slúži metóda indexOf(character), kde character je hľadaný znak. Táto metóda vracia index prvého výskytu daného znaku a ak ho v reťazci nenájde, vráti hodnotu -1.

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

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

Výstup:

Konzolová aplikácia
Java
v

A teraz sa pozrieme na zistenie indexu zadaného znaku:

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

Výstup:

Konzolová aplikácia
Java
2

Znaky na danej pozícii sú v Jave read-only, nemôžeme ich teda jednoducho zmeniť.

Samozrejme to ide urobiť inak, neskôr si to ukážeme, zatiaľ sa 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 metódou scanner.nextLine(). Reťazec budeme prechádzať cyklom po jednom znaku. Rovno tu zdôrazním, ž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ť, bude to dĺžka 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:

// the string that we want to analyze
String mountain = "Mount Everest";
System.out.println(mountain);
mountain = mountain.toLowerCase();

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

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

// the main loop
for (char character : mountain.toCharArray()) {

}

Pretože sa jedná o zložitejší kód, nebudeme zabúdať na komentáre.

Spočiatku si pripravíme reťazec a prevedieme ho na malé písmená. Počítadlá vynulujeme. Na definície znakov nám postačí obyčajný typ String. Hlavný cyklus nám prejde jednotlivé znaky v reťazci mountain. Aby sme mohli znaky iterovať (prechádzať cyklom), musíme si typ String previesť na pole znakov. V úvode som hovoril, že typ String vlastne pole znakov je, ale nie plnohodnotné. Obsahuje niečo navyše a niečo mu chýba, napr. možnosť prvky iterovať cyklom. V cykle teda na premennú mountain zavoláme metódu toCharArray(), ktorá vráti plnohodnotné pole znakov z reťazca mountain. V každej iterácii cyklu bude v premennej character aktuálny znak reťazca mountain.

Poďme plniť počítadlá, pre jednoduchosť už nebudem opisovať zvyšok kódu a presuniem sa len k cyklu:

// the main loop
for (char character : mountain.toCharArray()) {
    if (vowels.contains(String.valueOf(character))) {
        vowelsCount++;
    } else if (consonants.contains(String.valueOf(character))) {
        consonantsCount++;
    }
}

Metódu contains() na reťazci už poznáme, ako parameter je možné jej odovzdať podreťazec. Bohužiaľ nemôžeme odovzdať znak char, musíme teda znak previesť na String. Na to slúži vyššie uvedená metóda valueOf(). Daný znak našej vety teda najskôr skúsime vyhľadať v reťazci vowels a prípadne zvýšiť ich počítadlo. Ak v samohláskach (vowels) nie je, pozrieme sa do spoluhlások (consonants) a prípadne opätovne zvýšime ich počítadlo. Teraz nám chýba už iba výpis na koniec. V texte použijeme špeciálnu sekvenciu znakov %n (alebo \n), tá spôsobí odriadkovanie. Použitím sekvencie %n (namiesto \n) zaistíme cross-platform kompatibilitu. Java teda odriadkuje správne na MacOS a aj napríklad na Windows:

System.out.printf("Vowels: %d%n", vowelsCount);
System.out.printf("Consonants: %d%n", consonantsCount);
System.out.printf("Non-alphanumeric characters: %d%n", mountain.length() - (vowelsCount + consonantsCount));

Výstup programu:

Konzolová aplikácia
Mount Everest
Vowels: 5
Consonants: 7
Non-alphanumeric characters: 1

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 nebola 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 tiež 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, teraz sa používa Unicode (UTF-8) kódovanie, kde sú znaky reprezentované trochu iným spôsobom. Avšak v Jave máme stále možnosť pracovať s ASCII hodnotami jednotlivých znakov. Hlavná výhoda je v tom, že znaky sú uložené v tabuľke za sebou podľa abecedy. Napr. na pozícii 97 nájdeme znak a, na pozícii 98 znak b a podobne. Podobne je to s číslami, diakritické znaky tam budú 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 character; // character
int asciiValue; //  ordinal (ASCII) value of the character

// conversion from text to ASCII value
character = 'a';
asciiValue= (int)character;
System.out.printf("The character %c was converted to its ASCII value of %d%n", character, asciiValue);
// conversion from an ASCII value to text
asciiValue= 98;
character = (char)asciiValue;
System.out.printf("ASCII value of %d was converted to its textual value of %c", asciiValue, character);

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 preloží ako "ifmmp". 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 scanner.nextLine(). Šifra nepočíta s diakritikou, medzerami a interpunkčnými znamienkami. Diakritiku budeme bojkotovať a budeme predpokladať, že ju používateľ nebude zadávať. Ideálne by sme potom mali diakritiku pred šifrovaním odstrániť, taktiež aj čokoľvek iné okrem písmen:

// variable initialization
String originalMessage = "blackholesarewheregoddividedbyzero";
System.out.printf("Original message: %s%n", originalMessage);
String encryptedMessage = "";
int shift = 1;

// loop iterating over characters
for (char character : originalMessage.toCharArray()) {

}

// printing
System.out.printf("Encrypted message: %s%n", encryptedMessage);

Teraz sa presunieme dovnútra cyklu, prevedieme premennú so znakom character 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 ascii = (int)character;
ascii += shift;
character = (char)ascii;
encryptedMessage += character;

Výstup programu:

Konzolová aplikácia
Original message: blackholesarewheregoddividedbyzero
Encrypted message: cmbdlipmftbsfxifsfhpeejwjefecz{fsp

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

int ascii = (int)character;
ascii += shift;
// overflow control
if (ascii > (int)'z') {
    ascii -= 26;
}
character = (char)ascii;
encryptedMessage += character;

Pokiaľ ascii presiahne ASCII hodnotu 'z', znížime ju o 26 znakov (toľko znakov má anglická abeceda). Operátor -= vykoná to isté, ako keby sme napísali ascii = ascii - 26. Je to jednoduché a náš program je teraz funkčný. Všimnime si, že nikde nepoužívame priame kódy znakov, v podmienke je (int)z, aj keď by sme tam mohli napísať rovno 122. Je to z dôvodu, aby bol náš program plne zbavený explicitných ASCII hodnôt a aby bolo lepšie viditeľné, ako funguje. Cvične si skúste urobiť dešifrovanie :)

V nasledujúcom cvičení, Riešené úlohy k 13. lekcii Javy, 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é 15x (5.54 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Java

 

Predchádzajúci článok
Najčastejšie chyby Java nováčikov - Vieš pomenovať premenné?
Všetky články v sekcii
Základné konštrukcie jazyka Java
Preskočiť článok
(neodporúčame)
Riešené úlohy k 13. lekcii Javy
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
35 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