Přidej si svou IT školu do profilu a najdi spolužáky zde na síti :)

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

Java Základní konstrukce Textové řetězce v Javě podruhé - práce s jednotlivými znaky

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Pole v Javě, 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. Pro přístup k jednotlivým znakům slouží metoda charAt(x), kde x udává index znaku v řetězci (počínaje 0).

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

String s = "Ahoj ITnetwork";
System.out.println(s);
System.out.println(s.charAt(2));

Výstup:

Konzolová aplikace
Ahoj ITnetwork
o

Zklamáním může být, že 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 sc.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 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.";
System.out.println(s);
s = s.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 c : s.toCharArray()) {

}

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. Abychom mohli znaky iterovat (procházet cyklem), musíme si String převést na pole znaků. V úvodu jsem říkal, že 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 s zavoláme metodu toCharArray(), která vrátí plnohodnotné pole znaků z řetězce s. 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
for (char c : s.toCharArray()) {
        if (samohlasky.contains(String.valueOf(c))) {
                pocetSamohlasek++;
        }
        else if (souhlasky.contains(String.valueOf(c))) {
                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", ta způsobí odřádkování.

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", s.length() - (pocetSamohlasek + pocetSouhlasek));

Konzolová aplikace
Programátor se zasekne ve sprše, protože instrukce na šampónu byly: Namydlit, omýt, opakovat.
Samohlásek: 31
Souhlásek: 45
Nepísmenných znaků: 17

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é 255 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 (UTF8) 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 "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;
System.out.printf("Znak %c jsme převedli na ASCII hodnotu %d\n", c, i);
// Převedeme ASCII hodnotu na znak
i = 98;
c = (char)i;
System.out.printf("ASCII hodnotu %d jsme převedli na znak %c", i, c);

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 na š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 sc.nextLine(). Š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";
System.out.printf("Původní zpráva: %s\n", s);
String zprava = "";
int posun = 1;

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

}

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

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;

Konzolová aplikace
Původní zpráva: cernediryjsoutamkdebuhdelilnulou
Zašifrovaná zpráva: dfsofejszktpvubnlefcviefmjmovmpv

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čne 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í.

V příští lekci, Textové řetězce v Javě do třetice - Split, 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 715x (35.71 kB)
Aplikace je včetně zdrojových kódů v jazyce java

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
26 hlasů
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.
Miniatura
Předchozí článek
Pole v Javě
Miniatura
Všechny články v sekci
Základní konstrukce jazyka Java
Miniatura
Následující článek
Cvičení k 8. lekci Javy
Aktivity (5)

 

 

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

Avatar
Filip Gajdos
Člen
Avatar
Filip Gajdos:4. února 15:14

/*

  • To change this license header, choose License Headers in Project Properties.
  • To change this template file, choose Tools | Templates
  • and open the template in the editor.

*/
package znaky;
import java.util.Arrays;

/**
*

  • @author filip212

*/
public class Znaky {

/**

  • @param args the command line arguments

*/
public static void main(String[] args) {
// TODO code application logic here

// retazec ktory chcem analyzovat
String ss = "Programátor sa zasekne v sprche, pretože inštrukcie na šampónu boli: Namydliť, umyť, opakovať.";
System.out.prin­tln(ss);
ss = ss.toLowerCase();

//Inicializacia pocitadiel
int pocetSamohlasok = 0;
int pocetSpoluhlasok = 0;

// definicia pola samohlasok a spoluhlasok
String[] samohlasky = {"a", "e", "i", "o", "u", "ä", "á", "é", "í", "ó", "ú", "ia", "ie", "iu", "ô"};
String[] spoluhlasky = {"d", "t", "n", "l", "h", "ch", "g", "h", "k", "ď", "ť", "ň", "ľ", "c", "č", "ž", "š", "dz", "ďz", "m", "b", "p", "v", "z", "s", "f", "r"};

// Hlavny cyklus
for (char c : ss.toCharArray())
{
for (int i = 0; i<15; i++)
{
if (samohlasky[i]­.contains(Strin­g.valueOf(c)))
pocetSamohlasok++;
}

for (int i = 0; i <27; i++)
{
if (spoluhlasky[i]­.contains(Strin­g.valueOf(c)))
pocetSpoluhlasok++;
}
}

//Vypis na konci
System.out.prin­tf("Pocet samohlasok = %d\n", pocetSamohlasok);
System.out.prin­tf("Pocet spoluhlasok = %d\n", pocetSpoluhlasok);
System.out.prin­tf("Pocet nepismennych znakov = %d", ss.length() - (pocetSamohlasok + pocetSpoluhlasok));
}

}

Tento kod mi normalne funguje pokial to mam v cestine vetu aj samohlasky a spoluhlasky dostavam rovnake vysledky ako v clanku pokial to ale prehodim do SVK jayzka dostavam
run:
Programátor sa zasekne v sprche, pretože inštrukcie na šampónu boli: Namydliť, umyť, opakovať.
Pocet samohlasok = 58
Pocet spoluhlasok = 53
Pocet nepismennych znakov = -17BUILD SUCCESSFUL (total time: 0 seconds)

 
Odpovědět 4. února 15:14
Avatar
Michal813
Člen
Avatar
Michal813:10. března 13:36

toto >>> "ie", "iu", "dz", "ďz" <<<< nie sú samohlásky a ni spoluhlásky, ale dvojhlásky - a tie ASCII nepozná ....

 
Odpovědět 10. března 13:36
Avatar
Matěj Prášek:13. června 17:22

Moje řešení Caesara s českou diakritikou :-)

package caesar;

import java.util.Scanner;

public class Caesar {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in, "Windows-1250");

        //abeceda jako pole charů
        char[] alphabet = "aábcčdďeéěfghiíjklmnňoópqrřsštťuúůvwxyýzž".toCharArray();

        System.out.println("Zadejte text pro šifrování:");
        String text = sc.nextLine().toLowerCase();

        System.out.println("Zadejte číslo pro šifrování:");
        int number = Integer.parseInt(sc.nextLine());

        boolean found;

        for(char c : text.toCharArray())
        {
            found = false;
            for(int i = 0; i < alphabet.length; i++)
            {
                if (c == alphabet[i])
                {
                    found = true;
                    System.out.print(alphabet[(i+number)%alphabet.length]);
                    break;
                }
            }
            if(!found)
                System.out.print(c);
        }
        System.out.println();
    }

}
 
Odpovědět 13. června 17:22
Avatar
Michal Athanasios Devecka:21. června 18:32

všetkému rozumiem v tvojom príklade, len tomu výstupnému výrazu akosi nie. vysvetlil by si tu [(i+number)%al­phabet.length]); ? nerozumime tomu %alphabet.lenght

dik.

 
Odpovědět 21. června 18:32
Avatar
gcx11
Redaktor
Avatar
Odpovídá na Michal Athanasios Devecka
gcx11:22. června 9:33

Tomu se říká modulo a vrací zbytek po dělení tím číslem, v tomto případě velikost toho pole alphabet.

Pole má velikost 41 znaků, chceš například dělat posun o jeden znak z písmene 'ž'. Jeho pozice v pozice v poli je 40, takže:

(40+1)%41=41%41=0

dává pozici znaku po posunu, což je znak 'a'.

Taky by to šlo kontrolovat ifem, jestli jsi nejsi s indexem už mimo pole, ale toto je jednodušší.

 
Odpovědět  +1 22. června 9:33
Avatar
Michal Athanasios Devecka:26. června 10:42

Všecko jasné, gcx. Opäť som o niečo múdrejší. ešte raz dik.

 
Odpovědět 26. června 10:42
Avatar
Michal Stisek:7. srpna 9:58

Docela by mě zajímalo řešení, jak odstranit ze vstupu uživatele mezery, otazníky, vykřičníky, apod a až potom to zakódovat. Zkouším to dlouhou chvíli, ale na to mám asi malej skill, kdyby jste někdo věděl, budu rád.

např. vstup: Ahoj to jsem já, jak se daří?
pro zašifrování by z toho bylo: ahojtojsemjajak­sedari

Asi bych si nepovolené znaky uložil do pole a s nimi dál pracoval, jako že by se při výskytu ve vstupu nahradily, ale fakt nevím jak... :-/

Odpovědět 7. srpna 9:58
Jít pořád dál má smysl
Avatar
Michal Stisek:7. srpna 10:08

Jediné co jsem dokázal je odstranění mezer v textu, takovým primitivním způsobem, ale chce to nějak ty povolené nebo zakázané znaky:

public class CaesarovaSifra {

    public static void main(String[] args) {
        // TODO inicializace promennych
        Scanner sc = new Scanner(System.in, "UTF-8");
        System.out.println("Zadej větu k zakódování...");
        String vstup = sc.nextLine();
        String zakazane = " ";
        String povolene = "";
        String veta = vstup.toLowerCase().trim().replace(zakazane, povolene);
        System.out.printf("Text k zakódování: %s\n", veta);
        String zprava = "";
        System.out.println("Zadej klíč o kolik dojde k posunu...");
        int posun = Integer.parseInt(sc.nextLine());

        // cyklus procházející jednotlivé znaky
        for (char c : veta.toCharArray()){
            int i = (int)c;
            i += posun;
            // kontrola přetečení
            if (i > (int)'z'){
                i -= 26;
            }
            char znak = (char)i;
            zprava = zprava + znak;
        }

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

}
Odpovědět 7. srpna 10:08
Jít pořád dál má smysl
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na Michal Stisek
pocitac770:7. srpna 21:51

Tip: vzhledem k tomu, že všemožných znaků jsou stovky, ba tisíce, tak je k ničemu vpisovat seznam nepovolených znaků... jednoduše si uděláš pole s povolenými znaky, poté projedeš string znak po znaku, jestli se onen znak nenachází v poli povolených znaků, tak ho odstraníš... (zde narazíš na další problém, ale to až později... Uvidíš)

 
Odpovědět 7. srpna 21:51
Avatar
Jan Rypáček:18. srpna 8:29

Zatím z mého pohledu nejlépe vysvětlený článek, pomohl mi lépe pochopit i některá předchozí témata. :-)

 
Odpovědět 18. srpna 8:29
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 41. Zobrazit vše