NOVINKA - Online rekvalifikační kurz Python programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 10 - Textové řetězce ve Swift do třetice - Split a joined

V předešlém cvičení, Řešené úlohy k 9. lekci Swift, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

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 :)

Na řetězci můžeme používat mnoho metod nebo vlastností, které známe z pole. Jsou to např: first, last, index() a další.

Když si vytvoříme libovolnou proměnnou a napíšeme za ni tečku, Xcode nám zobrazí 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:

Nápověda metod na řetězci v Xcode - Základní konstrukce jazyka Swift

Tu samou nabídku lze vyvolat také stiskem Ctrl + Mezerník v případě, že textový 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. Xcode 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

insert()

Vloží podřetězec do řetězce na určitou pozici. Parametry jsou pozice v řetězci a podřetězec.

Klikni pro editaci
  • var text = "Já bych všechny ty internety zakázala."
    text.insert(contentsOf: "ne", at: text.index(text.startIndex, offsetBy: 29))
    print(text)
    
    • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

    Výstup:

    Já bych všechny ty internety nezakázala.

    remove() a removeSubrange()

    Metoda remove() je jednodušší, ale umí odstranit pouze jeden znak na námi zadaném indexu. Index opět musí být zadaný jako datový typ String.Index, který jsme již potkali. removeSubrange() odstraní libovolný podřetězec našeho řetězce, ale opět ho musíme zadat pomocí String.Index a aby toho nebylo málo, tak ještě přes datový typ Range.

    Range je prakticky interval "od-do" a zapisuje se pomocí trojice teček. Již jsme se s ním setkali u cyklů :-) for i in 1...5, pamatujete? Podobně v něm lze vytvořit Range z String.Index. Níže si ukážeme několik příkladů.

    Klikni pro editaci
    • var text = "Kdo se směje naposled, ten je admin."
      text.remove(at: text.startIndex)
      print(text)
      
      • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

      Výstup:

      do se směje naposled, ten je admin.

      A příklad na removeSubrange():

      Klikni pro editaci
      • var text = "Kdo se směje naposled, ten je admin."
        text.removeSubrange(text.index(text.startIndex, offsetBy: 3)..<text.endIndex)
        print(text)
        
        • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

        Výstup:

        Kdo

        Všimněte si, že v Range musíme použít operátor < (menší než), protože jinak bychom se přes endIndex dostali o jednu pozici mimo String a program by spadl.

        Substring

        Substring slouží k získání části řetězce ze String. Dřívější verze Swiftu nabízely stejnojmennou metodu. Ta je ale zastaralá a nově k získání substringu slouží hranaté závorky. Tomuto se říká "slicing", zkrátka takové krájení řetězců. Opět si ukážeme několik příkladů a pak je vysvětlíme.

        Klikni pro editaci
        • let text = "Kdo se směje naposled, ten je admin."
          let endIndex = text.index(of: ",")!
          let substring = String(text[...endIndex])
          print(substring)
          
          • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

          Výstup:

          Kdo se směje naposled,

          Zkusme si další příklad:

          Klikni pro editaci
          • let text = "Kdo se směje naposled, ten je admin."
            let startIndex = text.index(of: ",")!
            let substring = String(text[startIndex...])
            print(substring)
            
            • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

            Výstup:

            , ten je admin.

            A poslední:

            Klikni pro editaci
            • let text = "Kdo se směje naposled, ten je admin."
              let startIndex = text.index(text.startIndex, offsetBy: 3)
              let endIndex = text.index(startIndex, offsetBy: 9)
              let substring = String(text[startIndex...endIndex])
              print(substring)
              
              • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

              Výstup:

              se směje

              Jak sami určitě vidíte, opět musíme pracovat s typy String.Index, takže celý kód zabírá více řádků. Také jsme využili Range omezené pouze z jedné strany. Takto můžete vzít celý substring od určitého indexu či po určitý index. V posledním příkladu jsme využili standardní Range omezené z obou stran a také jsme endIndex získali za pomoci toho startovního.

              Všechny tyto slicing operace nevrací přímo String, ale typ Substring, který je nejlepší hned převést zpět na String. Doporučuje to přímo Apple v dokumentaci a jedním z důvodů je, že Substring si v paměti drží původní String, ačkoliv my s ním neplánujeme pracovat.

              dropLast(), dropFirst()

              Tyto metody nám dovolí odstranit znaky buď z konce nebo začátku řetězce. Dostupné je přetížení s parametrem k, který říká, kolik znaků má být odebráno. Pokud jej neuvedeme, je odebrán jeden znak. Metody vrací modifikovaný řetězec, takže ho nesmíte zapomenout použít :-)

              Metody vrací opět typ Substring, ne String.

              Klikni pro editaci
              • var text = "Ano!"
                let upraveny = text.dropLast(1)
                print(upraveny)
                
                • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

                Výstup:

                Ano

                compare()

                Metoda umožňuje porovnat dva řetězce podle abecedy. Výsledek porovnání získáme skrze vlastnost rawValue. Vrací -1 pokud je řetězec před řetězcem předaným v parametru, 0 pokud jsou stejné a 1 pokud je za ním:

                Klikni pro editaci
                • print("akát".compare("blýskavice").rawValue)
                  
                  • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

                  Výstup:

                  -1

                  Pojďme se nyní podívat na 2 další metody na String, které jsou opravdu velmi užitečné.

                  split() a joined()

                  Z předchozí lekce víme, že parsování řetězce znak 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 (řádek souboru nebo řádek 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 String můžeme volat metodu split(), která bere jako parametr separátor (typu Character). Následně původní řetězec rozdělí podle separátoru na pole podřetězců, které vrátí. To nám velmi ulehčí práci při rozdělování hodnot v řetězci.

                  Metoda joined() nám naopak umožňuje pole podřetězců spojit oddělovačem do jediného řetězce, parametr je oddělovač. Výstupem metody je výsledný řetězec. Metodu můžeme zavolat bez parametru a tím dojde ke spojení řetězců bez oddělovače.

                  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. Pro oba vzory použijeme pole.

                  Struktura našeho programu by nyní mohla vypadat následovně:

                  // řetězec, který chceme dekódovat
                  let s = ".. - -. . - .-- --- .-. -.-"
                  print("Původní zpráva: \(s)")
                  // řetězec s dekódovanou zprávou
                  var zprava = ""
                  
                  // vzorová pole
                  let 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"]
                  let morseovyZnaky = [".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....",
                      "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-",
                      "...-", ".--", "-..-", "-.--", "--.."]

                  Proč je abeceda také jako pole? Ušetří nám to spoustu starostí při hledání písmena podle morseova znaku. Jinak bychom museli řešit, jak rozumně převést Array.Index na String.Index, a kód by byl mnohem delší.

                  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 for..in:

                  // rozbití řetězce na znaky morzeovky
                  let znaky = s.split(separator: " ")
                  
                  // iterace znaky morzeovky
                  for morseuvZnak in 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 ve Swift 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 obě pole obsahují stejné znaky, seřazené dle abecedy. Umístěme do těla cyklu následující kód:

                  let index = morseovyZnaky.index(of: String(morseuvZnak))
                  
                  // Rozbalení Optional, znak nalezen
                  if let index = index {
                      zprava += abecedniZnaky[index]
                  }

                  Pokusíme se zjistit index morseova znaku. Pokud se to podaří, najdeme v abecedě odpovídající písmeno a to přidáme do zprávy. Operátor += nahrazuje zprava = zprava + abecedniZnak.

                  Závěrem samozřejmě zprávu vypíšeme:

                  Klikni pro editaci
                  • print("Dekódovaná zpráva: \(zprava)")
                    
                    • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

                    Výstup:

                    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() a joined() se potkáme během Swift kurzu 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:

                    Klikni pro editaci
                    • print("První řádka\nDruhá řádka")
                      
                      • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

                      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 \u{xxxx}, kde xxxx je kód znaku.

                      Problém může nastat ve chvíli, když chceme napsat samotné \, musíme ho tzv. odescapovat:

                      Klikni pro editaci
                      • print("Toto je zpětné lomítko: \\")
                        
                        • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

                        Stejným způsobem můžeme odescapovat např. uvozovku tak, aby ji Swift nechápal jako konec řetězce:

                        Klikni pro editaci
                        • print("Toto je uvozovka: \"")
                          
                          • Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.

                          Vstupy z konzole a polí v okenních aplikacích se samozřejmě escapují samy, aby uživatel nemohl zadat \n a podobně. V kódu to má programátor povoleno a musí na to myslet.

                          Víceřádkový String

                          Swift 4 umožňuje definovat String na více řádků bez přidávání \n. Slouží k tomu trojité uvozovky. Víceřádkový String se deklaruje stejně jako obyčejný String, po trojitých uvozovkách ale musí následovat nový řádek. Uvniř můžete používat uvozovky bez escapování, takže se to celé lépe čte:

                          let dlouhyText = """
                          Takto můžete mít hezky dlouhý text a zároveň
                          zachovat čitelnost nebo použít "uvozovky" jak chceme.
                          """
                          
                          print(dlouhyText)

                          Tímto jsme v podstatě zakončili sekci se základní strukturou jazyka Swift.

                          V následujícím cvičení, Řešené úlohy k 10. lekci Swift, si procvičíme nabyté zkušenosti z předchozích lekcí.


                           

                          Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

                          Stáhnout

                          Stažením následujícího souboru souhlasíš s licenčními podmínkami

                          Staženo 25x (33.33 kB)
                          Aplikace je včetně zdrojových kódů v jazyce Swift

                           

                          Jak se ti líbí článek?
                          Před uložením hodnocení, popiš prosím autorovi, co je špatněZnaků 0 z 50-500
                          Předchozí článek
                          Řešené úlohy k 9. lekci Swift
                          Všechny články v sekci
                          Základní konstrukce jazyka Swift
                          Přeskočit článek
                          (nedoporučujeme)
                          Řešené úlohy k 10. lekci Swift
                          Článek pro vás napsal Filip Němeček
                          Avatar
                          Uživatelské hodnocení:
                          10 hlasů
                          Autor se věnuje vývoji iOS aplikací (občas macOS)
                          Aktivity