# Řetězce a práce s nimi - Třída ```String``` - Jedna z nejpoužívanějších tříd z Java Core API - Řetězcové konstanty (literály – texty uzavřené do uvozovek, např. "Ahoj") používáme od začátku předmětu KIV/PPA1 ### Specifické vlastnosti třídy String - ```String``` je třída, jednotlivé řetězce (tj. texty) jsou její instance uložené na haldě (na heapu) - Z praktických důvodů má však některé vlastnosti, které u jiných tříd nejsou #### Vytvoření řetězce bez operátoru new - Jak bylo vidět ve všech příkladech, které dosud vypisovaly text, instance třídy ```String``` může vzniknout i bez operátoru ```new``` - Vznikne zápisem literálu (nepojmenované konstanty) - Např. ```String pozdrav = "Ahoj!";``` - Např. ```System.out.println("Nazdar!");``` - Tento postup jsme používali dosud (aniž jsme věděli o tom, že vznikají instance třídy ```String```) - Tento postup se používá běžně - Je možné i přímo vytvořit novou instanci třídy String operátorem ```new``` - Třída ```String``` obsahuje několik konstruktorů umožňující vytvořit řetězec z pole znaků, pole bytů, atd. - Je možné i vytvořit nový řetězec z jiného řetězce - Např. ```String pozdrav = new String("Ahoj!");``` - Tento zápis NEPOUŽÍVAT - Protože samotný zápis literálu "```Ahoj```" způsobí vytvoření nové instance třídy ```String```, volání new ```String()``` zbytečně vytvoří další instanci #### Konstantnost řetězce - Řetězce v Javě (tj. instance třídy ```String```) jsou konstantní, neměnné (immutable) - Nelze do nich přidávat text, mazat, nebo měnit jednotlivé znaky - Veškeré metody zdánlivě upravující řetězec ve skutečnosti vytvoří novou instanci třídy ```String``` a text v původní instanci zůstane nedotčen - Existují třídy pro práci s měnitelným řetězcem - Třída StringBuilder - Třída StringBuffer - Přesto si ve většině případů vystačíme s třídou ```String``` - To, že při změně obsahu řetězce dojde k vytvoření nové instance a původní zůstane nezměněná, nám nevadí (v podstatě si toho nevšimneme) #### Spojení řetězců pomocí operátoru „+“ (zřetězení) - Od začátku jsme běžně používali - Především v metodě ```System.out.println()``` - Operátor „+“ spojí dva řetězce (operandy) a vytvoří novou instanci obsahující texty obou řetězců (operandů) - Operátor „+“ také umožňuje připojit k řetězci libovolný datový typ - Hodnota daného typu se převede na řetězec - Alespoň jeden operand musí být řetězec, pokud ne, má operátor „+“ význam sčítání - Pro přehlednost je lepší začínat řetězcem, pokud nechceme začínat uvozujícím textem, lze použít prázdný řetězec - Časté je použití přímo v metodě ```System.out.println()``` ### Práce s řetězci - Metody pro práci s řetězci jsou převážně **metody instance třídy String** - Proto se volají nad referenční proměnnou ukazující na instanci řetězce - ```referenčníProměnná.názevMetody(parametry);``` - Např. ```pozdrav.charAt(0);``` - Stejné jako u jiných objektů - Protože instance řetězce vznikne pouhým zapsáním literálu, lze nad ním rovnou volat metody bez nutnosti použití referenční proměnné - Např. ```"Ahoj".charAt(0); //Funkcni ale nepouzivat``` - NEPOUŽÍVAT! #### Práce s jednotlivými znaky a délka řetězce - Řetězec si lze představit jako speciální konstantní pole znaků - Ale jedná se o instanci třídy, nelze s ní pracovat jako s polem – tj. přistupovat k jednotlivým znakům pomocí hranatých závorek „[“ a „]“ - Pole znaků (tj. ```char[]```) je uvnitř řetězce skutečně obsaženo - Pro následující příklady předpokládejte deklaraci - ```String pozdrav = "Nazdar";``` - Délka řetězce - Metoda ```length()``` - POZOR! – Je to metoda, nikoliv proměnná, jako u polí (tj. musí se uvést závorky) - Vrací počet znaků řetězce (včetně bílých znaků) - ```int delka = pozdrav.length(); //delka bude 6``` - Znak na zadané pozici - Metoda ```charAt(indexZnaku)``` - Znaky mají index podobně jako prvky pole číslované od 0 do délka řetězce - 1 - ```char znak = pozdrav.charAt(0); //znak bude 'N'``` - Index prvního výskytu znaku/podřetězce - Metoda ```indexOf(znak/podřetězec)``` - Vrátí index prvního výskytu znaku (podřetězce) v řetězci, nebo -1, pokud znak (podřetězec) není nalezen - ```int index1 = pozdrav.indexOf('a'); //index1 bude 1``` - ```int index2 = pozdrav.indexOf("zd"); //index2 bude 2``` - ```int index3 = pozdrav.indexOf("j"); //index3 bude -1``` - Index posledního výskytu znaku/podřetězce - Metoda ```lastIndexOf```(```znak/podřetězec```) - Vrátí index posledního výskytu znaku (podřetězce) v řetězci, nebo -1, pokud znak (podřetězec) není nalezen - ``int li1 = pozdrav.lastIndexOf('a'); //li1 bude 4`` - ```int li2 = pozdrav.lastIndexOf("Na "); //li2 bude 0 ``` - Náhrada všech výskytů znaku v řetězci - Metoda ```replace(puvodníZnak, novýZnak)``` - ```String s2 = pozdrav.replace('a', 'e'); //"Nezder"``` #### Porovnání řetězců - Řetězce jsou instance třídy ```String``` - Porovnání pomocí operátoru „```==```“, pouze zjistí, zda se jedná o stejné instance **nepoužívat** - **Porovnání podle obsahů** (důsledně včetně velikosti písmen) – **shodné/rozdílné** - Metoda ```equals```(```jinýŘetězec```) - Výsledek ```true``` (řetězce jsou shodné) nebo ```false``` (řetězce jsou rozdílné) - Porovnání podle obsahů **bez ohledu na velikost písmen** – **shodné/rozdílné** - Metoda ```equalsIgnoreCase```(```jinýŘetězec```) - Výsledek ```true``` (řetězce jsou shodné bez ohledu na velikost písmen) nebo ```false``` (řetězce jsou rozdílné bez ohledu na velikost písmen) - Porovnání podle obsahů (**důsledně včetně velikosti písmen**) – **větší/menší/stejný** - Metoda ```compareTo```(```jinýŘetězec```) - Výsledek 0 (řetězce jsou shodné), nebo záporné číslo (první řetězec je „menší“ než druhý – tj. je blíže začátku abecedy), nebo kladné číslo (první řetězec je „větší“ než druhý – tj. je dále od začátku abecedy) - Výsledek 0 (řetězce jsou shodné), nebo záporné číslo (první řetězec je „menší“ než druhý – tj. je blíže začátku abecedy), nebo kladné číslo (první řetězec je „větší“ než druhý – tj. je blíže konci abecedy) - POZOR! – Návratové hodnoty metod ```compareTo…()``` - Vrácené záporné a kladné číslo (když řetězce nejsou shodné) nemusí být (a není) jen -1 nebo 1 - Návratovou hodnotu metody je třeba porovnávat pomocí operátorů „>“ a „<“, nikoliv porovnáním operátorem „==“ s -1 či 1 - **Test začátku a konce řetězce** - Výsledek ```true```, pokud řetězec začíná/končí zadaným podřetězcem, jinak vrací ```false``` - Metoda ```startsWith(prefix)``` - Metoda ```endsWith(postFix)``` - Metoda ```startsWithIgnoreCase(prefix)``` - Metoda ```endsWithIgnoreCase(postFix)``` #### Získání podřetězce z řetězce - Stále předpokládáme deklaraci ```String pozdrav = "Nazdar";``` - Získání podřetězce od zadaného indexu (**včetně**) do konce - Metoda ```substring(indexOd)``` - ```String p1 = pozdrav.substring(2); //p1 bude "zdar"``` - Získání podřetězce od zadaného indexu (**včetně**) do zadaného indexu (**vyjma**) - Metoda ```substring(indexOd, indexDo)``` - ```String p2 = pozdrav.substring(0, 2); //p2 bude "Na"``` - Odstranění bílých znaků ze začátku a konce řetězce - Metoda ```trim()``` - Uvažujme deklaraci ```String bz = "\r\n \tahoj \t\n\r";``` - ```String oriznuty = bz.trim(); //oriznuty bude "ahoj"``` ### Konverze řetězce na jiné datové typy a obráceně - Relativně časté operace - **Převod řetězce na číselný datový typ** - Typicky při vstupu (když nepoužíváme Scanner, nebo je vstup komplikovanější, nebo v GUI) - Číselný datový typ je potřeba, abychom mohli provádět aritmetické operace - ```Např. "1" + "2" je řetězec "12", ale 1 + 2 je 3``` - **Převod čísla na řetězec** - Implicitně se provádí při každém výstupu - Explicitně, když chceme využít operace nad řetězci např. pro čísla (např. zjištění počtu znaků (tj. číslic), zřetězení s řetězcem) - **Převod instance (objektu) na řetězec** - Pokud chceme instanci vypsat #### Konverze řetězce na jiné datové typy - Pro základní datové typy se používá metoda ```Typ.parseTyp(řetězec)``` z příslušné obalovací třídy základního datového typu - Jedná se o metody třídy, volá se s názvem třídy, není potřeba vytvářet instanci - Např. ```int i = Integer.parseInt("42");``` - Např. ```long l = Long.parseLong("2444111333");``` - Např. ```double d = Double.parseDouble("5.972E24");``` - Např. ```boolean b = Boolean.parseBoolean("false");``` - Pro objekty - Není žádný standardní a obecný postup pro převod řetězce na instanci jiné třídy #### Konverze jiných datových typů na řetězec - Běžně se využívá schopnost operátoru zřetězení „+“ převádět jiné datové typy (základní datové typy a instance tříd) na řetězec - Pro základní datové typy funguje přímočaře - Pro objekty také – objekt se převede na řetězec implicitním voláním metody ```toString()``` - Pokud je metoda ```toString()``` překryta, funguje dobře - Pro pole je potřeba použít metodu třídy ```Arrays.toString(pole)``` - Pokud se použije pouze operátor „+“, použije se implicitní textová reprezentace pole (např. ```[I@15db9742```) - Pro základní datové typy existuje další možnost – metoda ```Typ.toString (hodnota)``` z příslušné obalovací třídy základního datového typu - Např. ```String s = Integer.toString(42); //s bude "42"``` - Pro základní datové typy a pro instance tříd existuje další možnost – metoda třídy ```valueOf(hodnota)``` třídy ```String``` - Metoda je překrytá pro všechny základní datové typy a objekty - Např. ```String s = String.valueOf(42) //s bude "42"``` ### Rozdělení řetězce na podřetězce - Často je potřeba **rozdělit řetězec na několik podřetězců** podle oddělovače (mezer, bílých znaků, čárek, teček, pomlček či jiné interpunkce či jiného znaku) - Metoda instance ```split(regulárníVýraz)``` ve třídě ```String``` - Metoda má jako parametr regulární výraz - Regulární výraz - Řetězec popisující celou množinu řetězců => mnohé znaky tak mají speciální význam - Parametrem tedy není jen znak, podle kterého se má řetězec rozdělit - Některé znaky (např. mezera) nemají speciální význam a lze je tak použít bez problémů (tj. ```split(" ")```) - Některé znaky mají speciální význam a je třeba použít tzv. „escape“ sekvenci - Zpětné lomítko „\“ před znak, který chceme použít => zruší se tím speciální význam znaku - Protože však zpětné lomítko má speciální význam i v řetězcových a znakových literálech (umožňuje napsat speciální znaky, např. konec řádky „\n“), je třeba napsat zpětná lomítka - Metoda vrací pole řetězců, délka pole záleží na počtu podřetězců dvě „\\“ ### Proměnný řetězec - V některých případech (ač jich není mnoho) opravdu potřebujeme, aby se při každé změně textu řetězce nevytvářela nová instance => je potřeba měnitelný (mutable) řetězec - Pokud provádíme v řetězci mnoho změn a použili bychom třídu ```String```, **vytvářelo by se mnoho instancí** – pro každou změnu se vytvoří nová, což zbytečně zabírá paměť a zpomaluje program (vytvoření instance nějakou dobu trvá) - Pokud by šel **řetězec měnit**, vystačíme si s jednou instancí #### Třída StringBuilder - **Proměnný/měnitelný řetězec** (sekvence znaků) - Vytvoření instance třídy ```StringBuilder``` - Lze zadat řetězec, nebo kapacitu - Kapacita se udává kvůli efektivitě – pokud je dostatečná, nemusí se během práce s řetězcem (při jeho prodloužení) kapacita navyšovat - Je však možné do instance třídy ```StringBuilder``` vložit/přidat řetězec nebo jiný datový typ (viz níže) a překročit aktuální kapacitu kapacita se automaticky navýší - Např. ```StringBuilder sb1 = new StringBuilder("Ahoj");``` - Např. ```StringBuilder sb2 = new StringBuilder(100);``` - Práce se znaky - Zjištění konkrétního znaku - Metoda ```charAt(index)``` - Vrátí znak na zadaném indexu - Např. ```int i = sb1.charAt(3); //'o'``` - Nastavení konkrétného znaku - Metoda ```setCharAt(index, znak)``` - Nastaví znak na zadaném indexu na zadaný znak - Např. ```sb1.setCharAt(0, 'E'); //"Ehoj"``` - Délka řetězce - Zjištění délky uloženého řetězce - Metoda ```getLength()``` - Vrátí délku řetězce - Např. ```int d1 = sb1.getLength(); //4``` - Např. ```int d2 = sb2.getLength(); //0``` - Nastavení délky řetězce - Metoda ```setLength(délka)``` - Nastaví délku řetězce - Pokud se řetězec prodlužuje, přidané znaky budou mít hodnotu 0 - Pokud se řetězec zkracuje, znaky nad zadanou délku se oříznou - Např. ```sb2.setLength(10); //10 znaků hodnoty 0``` - Např. ```sb2.setLength(0); //Prazdny retezec``` - Převrácení řetězce - Metoda ```reverse()``` - Převrátí pořadí znaků - Např. ```sb1.reverse(); //"johE"``` - Přidání/vložení libovolného datového typu - Přidání libovolného datového typu - Metoda ```append(hodnota)``` - Metoda překryta pro všechny základní datové typy, ```Object```, ```String``` a ```StringBuilder``` - Např. ```sb2.append(true); //"true"``` - Např. ```sb1.append(sb2); //"johEtrue"``` - Vložení libovolného datového typu - Metoda ```insert(index, hodnota)``` - Metoda překryta pro všechny základní datové typy, ```Object``` a ```String``` - Znaky na pozici ```index``` a dále se odsunou o počet vložených znaků - Např. ```sb1.insert(3, 42); //"joh42Etrue"``` - Smazání podřetězce či znaku - Smazání podřetězce - Metoda ```delete(začátek, konec);``` - Smaže podřetězec začínající na pozici začátek a končící před pozicí ```konec``` - Např. ```sb1.delete(2, 5); //"joEtrue"``` - Smazání znaku - Metoda ```deleteCharAt(index)``` - Smaže znak na zadaném indexu - Např. ```sb1.deleteCharAt(2); //"jotrue"``` #### Třída StringBuffer - Proměnný/měnitelný řetězec (sekvence znaků), která má téměř stejné metody a téměř stejné použití jako třída ```StringBuilder``` - Starší a pomalejší - Vhodný, pokud má s proměnným řetězcem pracovat více vláken (viz předmět KIV/PGS) ### Třída Character - Obalovací třída pro základní datový typ ```char``` - Obsahuje mnoho konstant důležitých pro práci s různými speciálními znaky (např. z jiných abeced než latinky) - Obsahuje mj. metody pro určení typu znaku #### Určení typu znaku - Metody pro určení typu znaku třídy ```Character``` - Jedná se o metody třídy, volají se nad názvem třídy, vrací ```true```, pokud znak je daného typu (podle názvu metody), jinak vrací ```false``` - Metody pracují správně i pro speciální znaky (které např. nejsou z latinky) - Metoda ```isDigit(znak)``` - Zjistí, jestli je zadaný znak číslice - Např. ```Character.isDigit('3'); //true``` - Např. ```Characet.isDigit('A'); //false``` - Metoda ```isLetter(znak)``` - Zjistí, jestli je zadaný znak písmeno - Např. ```Character.isLetter('3'); //false``` - Např. ```Character.isLetter('A'); //true``` - Metoda ```isLetterOrDigit(znak)``` - Zjistí, jestli je zadaný znak písmeno nebo číslice - Např. ```Character.isLetterOrDigit('3'); //true``` - Např. ```Character.isLetterOrDigit('A'); //true``` - Metoda ```isWhitespace(znak)``` - Zjistí, jestli je zadaný znak bílý znak - POZOR! – druhé „```s```“ v názvu metody je také malé - Např. ```Character.isWhitespace(' '); //true``` - Např. ```Character.isWhitespace('\n'); //true``` - Metoda ```isLowerCase(znak)``` - Zjistí, jestli je zadaný znak malé písmeno - Např. ```Character.isLowerCase('a'); //true``` - Např. ```Character.isLowerCase('A'); //false``` - Např. ```Character.isLowerCase('3'); //false``` - Metoda ```isUpperCase(znak)``` - Zjistí, jestli je zadaný znak velké písmeno - Např. ```Character.isUpperCase('a'); //false``` - Např. ```Character.isUpperCase('A'); //true``` - Např. ```Character.isUpperCase('3'); //false```