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

17 KiB
Raw Permalink Blame History

Ř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