FAV-ZCU/KIV PPA1/10. Řetězce.md

288 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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