Letní akce! Lákají tě IT školení C#, Javy a PHP v Brně? Přihlas se a napiš nám do zpráv kód "BRNO 500" pro slevu 500 Kč na libovolný brněnský kurz. Lze kombinovat se slevami uvedenými u školení i použít pro více kurzů. Akce končí 28.7.

Lekce 9 - Textové řetězce v Javě do třetice - Split

Java Základní konstrukce Textové řetězce v Javě do třetice - Split

ONEbit hosting Unicorn College 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, Textové řetězce v Javě podruhé - práce s jednotlivými znaky, jsme si ukázali, že String je vlastně pole znaků. Dnes si vysvětlíme další metody na řetězci, které jsem vám záměrně zatajil, protože jsme nevěděli, že String je vlastně pole :)

Když si vytvoříme libovolnou proměnnou a napíšeme za ni poté tečku, zobrazí nám NetBeans nabídku všech metod a vlastností (a také proměnných, ale k tomu se dostaneme až u objektů), které na ni můžeme volat. Zkusme si to:

Metody na textovém řetězci string v NetBeans

Tu samou nabídku lze vyvolat také stiskem CTRL + Mezerník v případě, že kurzor umístíme na tečku. Samozřejmě to platí pro všechny proměnné i třídy a budeme toho využívat stále častěji. Metody jsou řazené abecedně a můžeme jimi listovat pomocí kurzorových šipek. NetBeans nám zobrazuje popis metod (co dělají) a jaké vyžadují parametry.

Řekněme si o následujících metodách a ukažme si je na jednoduchých příkladech:

Další metody na řetězci

Substring()

Vrátí podřetězec od dané počáteční pozice do dané koncové pozice.

System.out.println("Kdo se směje naposled, ten je admin.".substring(13, 21));

Výstup:

Konzolová aplikace
naposled

CompareTo()

Umožňuje porovnat dva řetězce podle abecedy. Vrací -1 pokud je první řetězec před řetězcem v parametru, 0 pokud jsou stejné a 1 pokud je za ním:

System.out.println("akát".compareTo("blýskavice"));

Výstup:

Konzolová aplikace
-1

Pojďme se nyní podívat na další metodu na Stringu, která je opravdu velmi užitečná.

Split()

Z předchozího tutoriálu víme, že parsování řetězce po znaku může být někdy docela složité a to jsme dělali poměrně jednoduchý příklad. S řetězci se samozřejmě budeme setkávat stále, a to jak na vstupu od uživatele (např. z konzole nebo z polí v okenních aplikacích), tak v souborech TXT a XML. Velmi často máme zadán jeden delší String (řádka souboru nebo řádka konzole), ve kterém je více hodnot, oddělených tzv. separátory, např. čárkou. V tomto případě hovoříme o formátu CSV (Comma-Separated Values, tedy hodnoty oddělené čárkou). Abychom si byli jisti, že víme, o čem hovoříme, ukažme si nějaké ukázkové řetězce:

Jan,Novák,Dlouhá 10,Praha 3,130 00
.. ... .-.. .- -. -.. ... --- ..-. -
(1,2,3;4,5,6;7,8,9)

První řetězec je očividně nějaký uživatel, takto bychom mohli např. realizovat uložení uživatelů do CSV souboru, každý na jeden řádek.
Druhý řetězec jsou znaky Morseovy abecedy, separátor (oddělovač) je zde mezera.
Třetí řetězec je matice o 3 sloupcích a 3 řádcích. Oddělovač sloupců je čárka, řádků středník

Na stringu můžeme volat metodu split(), která bere jako parametr separátor. Následně původní řetězec rozdělí podle separátorů na pole podřetězců, které vrátí. To nám velmi ulehčí práci při rozdělování hodnot v řetězci.

Jelikož neumíme tvořit objekty (uživatele) a ani pracovat s vícerozměrnými poli (matice), zkusíme si naprogramovat dekodér zpráv z Morseovy abecedy.

Dekodér Morseovy abecedy

Pojďme si opět připravit strukturu programu. Budeme potřebovat 2 řetězce se zprávou, jeden se zprávou v Morseově abecedě, druhý zatím prázdný, do kterého budeme ukládat výsledek našeho snažení. Dále budeme jako v případě samohlásek potřebovat nějaký vzor písmen. K písmenům samozřejmě vzor jejich znaku v morzeovce. Písmena můžeme opět uložit do pouhého Stringu, protože mají jen jeden znak. Znaky Morseovy abecedy mají již znaků více, ty musíme dát do pole. Struktura našeho programu by nyní mohla vypadat následovně:

// řetězec, který chceme dekódovat
String s = ".. - -. . - .-- --- .-. -.-";
System.out.printf("Původní zpráva: %s\n", s);
// řetězec s dekódovanou zprávou
String zprava = "";

// vzorová pole
String abecedniZnaky = "abcdefghijklmnopqrstuvwxyz";
String[] morseovyZnaky = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....",
"..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-",
"...-", ".--", "-..-", "-.--", "--.."};

Můžete si potom přidat další znaky jako čísla a interpunkční znaménka, my je zde vynecháme. Nyní si řetězec s rozbijeme metodou split() na pole podřetězců, obsahujících jednotlivé znaky morzeovky. Splitovat budeme podle znaku mezery. Pole následně proiterujeme cyklem foreach:

// rozbití řetězce na znaky morzeovky
String[] znaky = s.split(" ");

// iterace znaků morzeovky
for (String morseuvZnak : znaky) {

}

Ideálně bychom se měli nějak vypořádat s případy, kde uživatel zadá např. více mezer mezi znaky (to uživatelé rádi dělají). Split() poté vytvoří o jeden řetězec v poli více, který bude prázdný. Ten bychom měli poté v cyklu detekovat a ignorovat, my se s tím v tutoriálu nebudeme zabývat.

V cyklu se pokusíme najít aktuálně čtený znak morzeovky v poli morseovyZnaky. Bude nás zajímat jeho index, protože když se podíváme na ten samý index v poli abecedniZnaky, bude tam odpovídající písmeno. To je samozřejmě z toho důvodu, že jak pole tak řetězec obsahují stejné znaky, seřazené dle abecedy. Umístěme do těla cyklu následující kód:

char abecedniZnak = '?';

int index = -1;
for (int i = 0; i < morseovyZnaky.length; i++) {
        if (morseovyZnaky[i].equals(morseuvZnak))
                index = i;
}

if (index >= 0) { // znak nalezen
        abecedniZnak = abecedniZnaky.charAt(index);
}
zprava += abecedniZnak;

Kód nejprve do abecedního znaku uloží '?', protože se může stát, že znak v naší sadě nemáme. Následně se pokusíme zjistit jeho index. Pole v Javě bohužel nemá metodu indexOf() a zatím nechci zabíhat do složitějších datových struktur. Napíšeme si tedy vyhledání Stringu v poli sami, bude to docela jednoduché.

Nejprve nastavíme index na -1, protože nevíme, jestli pole daný String (morzeův znak) obsahuje. Následně pole projedeme cyklem a budeme kontrolovat jednotlivé Stringy s naším stringem (znakem). Již víme, že musíme k porovnání dvou řetězců použít metodu equals(). Pokud řetězce souhlasí, uložíme si aktuální index.

Pokud jsme znak našli (index >= 0), dosadíme do abecedniZnak znak z abecedních znaků pod tímto indexem. Nakonec znak připojíme ke zprávě. Operátor += nahrazuje zprava = zprava + abecedniZnak.

Závěrem samozřejmě zprávu vypíšeme:

System.out.printf("Dekódovaná zpráva: %s\n", zprava);

Konzolová aplikace
Původní zpráva: .. - -. . - .-- --- .-. -.-
Dekódovaná zpráva: itnetwork

Hotovo! Za úkol máte si naprogramovat program opačný, který naopak zakóduje řetězec do morzeovky, kód bude velmi podobný. Se split() se potkáme během seriálu ještě několikrát.

Speciální znaky a escapování

Textový řetězec může obsahovat speciální znaky, které jsou předsazené zpětným lomítkem "\". Je to zejména znak \n, který kdekoli v textu způsobí odřádkování a poté \t, kde se jedná o tabulátor. Pojďme si to vyzkoušet:

System.out.println("První řádka\nDruhá řádka");

Znak "\" označuje nějakou speciální sekvenci znaků v řetězci a je dále využíván např. k psaní unicode znaku jako "\uxxxx", kde xxxx je kód znaku.

Problém může nastat ve chvíli, když chceme napsat samotné "\", musíme ho tzv. odescapovat:

System.out.println("Toto je zpětné lomítko: \\");

Stejným způsobem můžeme odescapovat např. uvozovku tak, aby ji Java nechápala jako konec řetězce:

System.out.println("Toto je uvozovka: \"");

Vstupy z konzole a polí v okenních aplikacích se samozřejmě escapují sami, aby uživatel nemohl zadat \n a podobně. V kódu to má programátor povoleno a musí na to myslet.

Tímto jsme v podstatě zakončili kurz se základní strukturou jazyka Java. V příštích lekcích si uvedeme vícerozměrná pole a matematické operace. Ze základních konstrukcí jazyka vás tu ale již nic nepřekvapí :) V podstatě byste již klidně mohli jít i na objekty, doporučují ale zbylé tutoriály ještě alespoň projet, jedná se přeci jen stále o základní znalosti, které byste měli mít. V příští lekci, Vícerozměrná pole v Javě, se tedy uvidíme u vícerozměrných polí.


 

Stáhnout

Staženo 779x (20.2 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?
11 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 sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity (8)

 

 

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

Avatar
Tereza Přibáňová:28.11.2017 21:13

Tak už jsem na to přišla :D ... Nejradši bych to smazala, ale nechám to tu jako odstrašující případ ;)

 
Odpovědět 28.11.2017 21:13
Avatar
zitekv
Člen
Avatar
zitekv:28.11.2017 21:35

Ahoj,
pokud ti to nefunguje je dobré říci více o tom co nefunguje a co "reklamuješ".
Určitě to něco hlásí - a to je většinou zásadní pro hledání příčiny tzn. pokud se ptáš na problém je dobré přiložit chybové hlášení z IDE.

 
Odpovědět 28.11.2017 21:35
Avatar
Odpovídá na zitekv
Tereza Přibáňová:29.11.2017 21:59

Nic to nehlásilo, ale všechny znaky se vypsaly 26x ... Ale děkuju z upozornění, příště budu vědět :)

 
Odpovědět 29.11.2017 21:59
Avatar
MiroslavP
Člen
Avatar
MiroslavP:21. ledna 19:44

Díky skvělému seriálu Java jsem opět objevil kouzlo programování. Děkuji MiP

Program dekoder Morse:
public class JavaApplication40 {

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Scanner sc = new Scanner(System­.in,"Windows-1250");
Vzorové soubory znaků:
String[] abecedniZnaky = {"A","B","C",­"D","E","F","G","H","I",­"J","K","L","M","N","O",­"P","Q","R","S",
"T","U","V","­W","X","Y","Z"};
String[] morseSoubor = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....",
"..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-",
"...-", ".--", "-..-", "-.--", "--.."};
String oddelovac = " "; //oddělovač mezi Morse znaky (mezera)
//Morse zpráva k dekódování, jednotlive znaky jsou odděleny mezerou
String abecedniZprava = "ITNETWORK";
String kodovanaZprava = "";
int index = -1;
pořadí morse znaku v souboru v přépadě shody je rúzné od -1
int j = 0; //slouží pro iteraci v abecednim souboru
System.out.prin­tln("Kodér Morseovy abecedy");
System.out.prin­t("Zpráva k zakódování: ");
System.out.prin­tln(abecedniZ­prava);
//Rozdělení Abecední zprávy na jednotlivé Abecední znaky
String[] znaky = abecedniZprava­.split("");
int l = znaky.length;
for (int i=0; i < l; i++){
// System.out.prin­tln(znaky[i]);
}
//iterace Jednotlivých abecednich znaků a přiřazení příslušného Morse znaku
for (int i = 0; i < l ;i++){
while (index < 0){
if (znaky[i].equ­als(abecedniZ­naky[j]))
{
index = j;
kodovanaZprava = kodovanaZprava + String.format("%s%s",m­orseSoubor[in­dex],oddelovac);
}
else{
j++;
}
}
index = -1;
j = 0;
}
System.out.prin­tln("Kódovaná zpráva: " +kodovanaZprava);
}
}

 
Odpovědět 21. ledna 19:44
Avatar
Moni
Člen
Avatar
Moni:19. března 9:11

Když to udělám podobným způsobem tak mi to jede, ale já bych potřebovala jak převést char na string.
for(char aZnak : slova.toCharA­rray()){
for(int i=0;i< abeceda.length;i++){

if(aZnak == abeceda[i]){

 
Odpovědět 19. března 9:11
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na Moni
pocitac770:20. března 1:14

Pokud chceš String z takto více znaků, stačí klasicky "přičítat" char ke Stringu

String s = "";
//....
s += aZnak;

Pokud ale výslovně postřebuješ z jednoho charu String (což nedává v takovémto příkladu smysl), tak se jedná o

String s = String.valueOf(aZnak);
Editováno 20. března 1:15
 
Odpovědět 20. března 1:14
Avatar
Moni
Člen
Avatar
Moni:20. března 7:28

Jo, super, už to jede. Jedna se o předělání většího počtu prográmků a tak se mi to nechtělo celé rozkopávat. A tohle je nejjednodušší. Díky moc.

 
Odpovědět 20. března 7:28
Avatar
Josef Pospíšil:6. dubna 13:06

V článku je, že:

CompareTo()
Umožňuje porovnat dva řetězce podle abecedy. Vrací -1 pokud je první řetězec před řetězcem v parametru, 0 pokud jsou stejné a 1 pokud je za ním:

Ale já když to zkoušel, tak to háže jiné číslo a to v závislosti na tom jak daleko jsou abecedně od sebe. Takže -1 a 1 je to jen v případě, že se porovnávají slova, jejichž písmena abecedně sousedí jinak je tam třeba mezi "akát" a "kráva" -10.

 
Odpovědět 6. dubna 13:06
Avatar
dominik_antus:13. května 15:02
Scanner sc = new Scanner(System.in);
                System.out.println("zdj ret: ");
                String mes = sc.nextLine();
                String dek = "";
                String abc = "abcdefghijklmnopqrstuvwxyz";
                String[] mor = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....",
                                "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-",
                                "...-", ".--", "-..-", "-.--", "--.."};
                mes.toLowerCase();
                //mes.trim();
                for(char znk : mes.toCharArray()) {
                        String mozn = null;
                        int index = 0;
                        for(int i = 0; i< mes.length(); i++) {
                                if(String.valueOf(abc.charAt(i)).equals(String.valueOf(znk)))
                                        index = i;
                                }
                                if(index >= 0) {
                                        dek += mor[index];
                                }
                        }
                        System.out.print(dek);
 
Odpovědět 13. května 15:02
Avatar
dominik_antus:15. května 18:58

Mohlo by to byť aj takto? Myslím, že všetko funguje ako má. :)

Scanner sc = new Scanner(System.in);
               System.out.println("vety: ");
               String vty = sc.nextLine();
               String[] sml = {":) " ,":D ", ":P "};
               int smll = 0;
               String[] vt = vty.split(" ");
               String vs = "";
               String inte = "!.?";
               for(String v : vt) {
                   for(char h: inte.toCharArray()) {
                          if(String.valueOf(v).contains(String.valueOf(h))) {
                                  if(smll == 3) {
                                          smll = 0;
                                          vs += v.replace(String.valueOf(h), String.valueOf(sml[smll]));
                                  smll++;  break;
                                  }
                                  else {
                                   vs += v.replace(String.valueOf(h), String.valueOf(sml[smll]));
                                  smll++;
                                 }
                          }
                   }
            }
        System.out.println(vs);
 
Odpovědět 15. května 18:58
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 42. Zobrazit vše