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:

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.
{SWIFT} var text = "Já bych všechny ty internety zakázala." text.insert(contentsOf: "ne", at: text.index(text.startIndex, offsetBy: 29)) print(text) {/SWIFT}
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ů.
{SWIFT} var text = "Kdo se směje naposled, ten je admin." text.remove(at: text.startIndex) print(text) {/SWIFT}
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()
:
{SWIFT} var text = "Kdo se směje naposled, ten je admin." text.removeSubrange(text.index(text.startIndex, offsetBy: 3)..<text.endIndex) print(text) {/SWIFT}
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.
{SWIFT} let text = "Kdo se směje naposled, ten je admin." let endIndex = text.index(of: ",")! let substring = String(text[...endIndex]) print(substring) {/SWIFT}
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:
{SWIFT} let text = "Kdo se směje naposled, ten je admin." let startIndex = text.index(of: ",")! let substring = String(text[startIndex...]) print(substring) {/SWIFT}
Zkontroluj, zda výstupy programu odpovídají předloze. S jinými texty testy neprojdou.
Výstup:
, ten je admin.
A poslední:
{SWIFT} 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) {/SWIFT}
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
.
{SWIFT} var text = "Ano!" let upraveny = text.dropLast(1) print(upraveny) {/SWIFT}
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:
{SWIFT} print("akát".compare("blýskavice").rawValue) {/SWIFT}
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:
{SWIFT} // ř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 = [".-", "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-", "...-", ".--", "-..-", "-.--", "--.."] let znaky = s.split(separator: " ") // iterace znaků morzeovky for morseuvZnak in znaky { let index = morseovyZnaky.index(of: String(morseuvZnak)) // Rozbalení Optional, znak nalezen if let index = index { zprava += abecedniZnaky[index] } } print("Dekódovaná zpráva: \(zprava)") {/SWIFT}
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:
{SWIFT} print("První řádka\nDruhá řádka") {/SWIFT}
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:
{SWIFT} print("Toto je zpětné lomítko: \\") {/SWIFT}
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:
{SWIFT} print("Toto je uvozovka: \"") {/SWIFT}
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