14. 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:
{JAVA_CONSOLE}
String language = "Java";
System.out.println(language);
System.out.println(language.charAt(2));
{/JAVA_CONSOLE}
Výstup:
Konzolová aplikácia
Java
v
A teraz sa pozrieme na zistenie indexu zadaného znaku:
{JAVA_CONSOLE}
String language = "Java";
System.out.println(language);
System.out.println(language.indexOf('v'));
{/JAVA_CONSOLE}
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ý, reťazec
nahradíme metódou scanner.nextLine(). 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ť, 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:
{JAVA_CONSOLE}
// 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()) {
if (vowels.contains(String.valueOf(character))) {
vowelsCount++;
} else if (consonants.contains(String.valueOf(character))) {
consonantsCount++;
}
}
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));
{/JAVA_CONSOLE}
Výstup programu:
Konzolová aplikácia
Mount Everest
Vowels: 5
Consonants: 7
Non-alphanumeric characters: 1
Spätné lomítko \ by sme na slovenskej
klávesnici napísali stlačením Pravého Alt + Q:

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. Dnes sa
používa Unicode (UTF-8) kódovanie, v ktorom sú znaky reprezentované trochu
iným spôsobom. Napriek tomu máme v Jave stále možnosť pracovať s ASCII
hodnotami jednotlivých znakov. Hlavnou výhodou je, ž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 tak
ďalej. Podobne je to s číslami, diakritické znaky sú však 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ť:
{JAVA_CONSOLE}
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);
{/JAVA_CONSOLE}
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:
{JAVA_CONSOLE}
// 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()) {
int ascii = (int)character;
ascii += shift;
character = (char)ascii;
encryptedMessage += character;
}
// printing
System.out.printf("Encrypted message: %s%n", encryptedMessage);
{/JAVA_CONSOLE}
Výstup programu:
Konzolová aplikácia
Original message: blackholesarewheregoddividedbyzero
Encrypted message: cmbdlipmftbsfxifsfhpeejwjefecz{fsp
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 aj ďalšie škaredé znaky. Uzavrieme
preto 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.
Máme hotový program, ktorý správu zašifruje. Poďme si teraz pomocou AI vytvoriť dešifrovanie.
Zadajme do Copilota tento prompt: "Vytvor mi program, ktorý Caesarovu šifru dešifruje. Chcem zachovať podobnú štruktúru programu a len obrátiť logiku posunu."
Copilot potom pripraví program podobný tomuto:
{JAVA_CONSOLE}
// variable initialization
String encryptedMessage = "cmbdlipmftbsfxifsfhpeejwjefecz{fsp";
System.out.printf("Encrypted message: %s%n", encryptedMessage);
String originalMessage = "";
int shift = 1;
// loop iterating over characters
for (char character : encryptedMessage.toCharArray()) {
int asciiValue = (int) character;
asciiValue -= shift;
// underflow control
if (asciiValue < (int) 'a') {
asciiValue += 26;
}
character = (char) asciiValue;
originalMessage += character;
}
// printing
System.out.printf("Original message: %s%n", originalMessage);
{/JAVA_CONSOLE}
Zhrnutie lekcie
Textový reťazec v Jave môžeme brať ako rad znakov. Jednotlivý znak
získame metódou charAt(index) a pozíciu znaku zistíme pomocou
indexOf(). Keď chceme prejsť všetky znaky, môžeme reťazec
previesť na pole znakov pomocou toCharArray(). To sa hodí
napríklad pri počítaní samohlások, spoluhlások a ostatných znakov vo
vete. Znak char je možné tiež previesť na číselnú ASCII
hodnotu a späť, takže s písmenami môžeme počítať. Vďaka tomu sa dá
urobiť napríklad Caesarova šifra, kde sa každé písmeno posunie o niekoľko
miest v abecede.
V nasledujúcom cvičení, Riešené úlohy k 14. 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é 75x (11.71 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Java

David sa informačné technológie naučil na