# Ř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```

