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

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

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 788x (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?
13 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 (11)

 

 

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

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
dommynyk
Člen
Avatar
dommynyk: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
dommynyk
Člen
Avatar
dommynyk: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
_________________________
Avatar
Monika Badačová:14. srpna 18:46

Prosím, neviete poradiť, kde je chyba?

String s = "ahoj";
System.out.prin­tf("Pôvodná správa: %s\n", s);

String sprava = "";

String []abecedneZnaky = {"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[] morseoveZnaky = {".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....",
"..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-",
"...-", ".--", "-..-", "-.--", "--.."};

String []znaky = s.split(" ");

for (String abecedniZnak : znaky) {
char morseovZnak = '?';

int index = -1;
for (int i = 0; i < abecedneZnaky­.length; i++) {
if (abecedneZnaky[i]­.equals(abeced­niZnak))
index = i; }

if (index >= 0) { // znak nalezen
morseovZnak = morseoveZnaky[in­dex]; } //systém vypisuje: incompatible types: String cannot be converted to char
sprava += morseovZnak+" "; }
System.out.prin­tf("Dekódovaná zpráva: \n %s\n", sprava); } }

 
Odpovědět 14. srpna 18:46
Avatar
Odpovídá na Monika Badačová
Matúš Olejník:14. srpna 19:23

Ahoj problém je v tom, že morseovZnak máš typu char a morseoveZnaky je pole Stringov a teda morseoveZnaky[in­dex] bude typu String. Jedno riešenie by mohlo byť že morseovZnak prerobíš na typ String

Ak by si sa nudila, tu som ti napísal kratšiu verziu aj s komentármi :)

public String stringToMorseEncode(String msg) {

//    ASCII hodnota znaku A je 65 preto nepotrebujeme takto rucne ukladat pismena do pola
//    String[] abecedneZnaky = {"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"};

    //TODO pracovat s parametrom funkcie
    String povodnaSprava = "ahoj";
    System.out.printf("Pôvodná správa: %s\n", povodnaSprava);

    String vyslednaSprava = "";

    String[] morseoveZnaky = {
            ".-", "-...", "-.-.", "-..", ".",
            "..-.", "--.", "....", "..", ".---",
            "-.-", ".-..", "--", "-.", "---",
            ".--.", "--.-", ".-.", "...", "-",
            "..-", "...-", ".--", "-..-", "-.--",
            "--.."
    };

    //TODO doplnit kontrolu na ine znaky nez pismena atd
    for(int i = 0; i < povodnaSprava.length(); i++) {
        //String na pozicii i v povodnaSprava prevedieme na uperCase znak pretoze nizsie
        //od jeho ASCII hodnoty odratavame ASCII hodnotu znaku 'A' a vysledok pouzijeme ako
        //index do pola morseoveZnaky
        char znak = Character.toUpperCase(povodnaSprava.charAt(i));
        int asciiHodnota = (int) znak;

        //TODO pouzit StringBuilder - pozri Google ;)
        vyslednaSprava += morseoveZnaky[asciiHodnota - 65]; // alebo asciiHodnota - (int) 'A'
        vyslednaSprava += " ";
    }

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

    return vyslednaSprava;
}
Editováno 14. srpna 19:25
Odpovědět  +1 14. srpna 19:23
/* I am not sure why this works but it fixes the problem */
Avatar
Monika Badačová:14. srpna 19:30

super :) ďakujem ti

 
Odpovědět  +1 14. srpna 19:30
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 45. Zobrazit vše