6. Znakové reťazce

Typ string

Čo už vieme o znakových reťazcoch:

  • reťazec je postupnosť znakov uzavretá v apostrofoch '' alebo v úvodzovkách ""

  • vieme priradiť reťazec do premennej

  • zreťaziť (zlepiť) dva reťazce

  • násobiť (zlepiť viac kópií) reťazca

  • načítať zo vstupu (pomocou input()) a vypisovať (pomocou print())

  • vyrobiť z čísla reťazec (str()), z reťazca číslo (int(), float())

  • rozobrať reťazec vo for-cykle

Postupne prejdeme tieto možnosti práce s reťazcami a doplníme ich o niektoré novinky.

Keďže znakový reťazec je postupnosť znakov uzavretá v apostrofoch '' alebo v úvodzovkách "", platí:

  • môže obsahovať ľubovoľné znaky (okrem znaku apostrof ' v '' reťazci, a znaku úvodzovka " v úvodzovkovom "" reťazci)

  • musí sa zmestiť do jedného riadka (nesmie prechádzať do druhého riadka)

  • môže obsahovať špeciálne znaky (zapisujú sa dvomi znakmi, ale pritom v reťazci reprezentujú len jeden), vždy začínajú znakom \ (opačná lomka):

    • \n - nový riadok

    • \t - tabulátor

    • \' - apostrof

    • \" - úvodzovka

    • \\ - opačná lomka

Napríklad

>>> 'Monty\nPython'
'Monty\nPython'
>>> print('Monty\nPython')
Monty
Python
>>> print('Monty\\nPython')
Monty\nPython

Viacriadkové reťazce

platí:

  • reťazec, ktorý začína trojicou buď apostrofov ''' alebo úvodzoviek """ môže obsahovať aj ' a ", môže prechádzať cez viac riadkov (automaticky sa sem doplní \n)

  • musí byť ukončený rovnakou trojicou ''' alebo """

    >>> macek = '''Išiel Macek
    do Malacek
    šošovičku mlácic'''
    >>> macek
    'Išiel Macek\ndo Malacek\nšošovičku mlácic'
    >>> print(macek)
    Išiel Macek
    do Malacek
    šošovičku mlácic
    >>> '''tento retazec obsahuje " aj ' a funguje'''
    'tento retazec obsahuje " aj \' a funguje'
    >>> print('''tento retazec obsahuje " aj ' a funguje''')
    tento retazec obsahuje " aj ' a funguje
    

Dĺžka reťazca

Štandardná funkcia len() vráti dĺžku reťazca (špeciálne znaky ako '\n', '\'', a pod. reprezentujú len 1 znak):

>>> a = 'Python'
>>> len(a)
6
>>> len('Peter\'s dog')
11
>>> len('\\\\\\')
3

Túto funkciu už vieme naprogramovať aj sami, ale v porovnaní so štandardnou funkciou len() bude oveľa pomalšia:

def dlzka(retazec):
    pocet = 0
    for znak in retazec:
        pocet += 1
    return pocet
>>> dlzka('Python')
6
>>> a = 'x' * 100000000
>>> dlzka(x)
100000000
>>> len(x)
100000000

Operácia in

Aby sme zistili, či sa v reťazci nachádza nejaký konkrétny znak, doteraz sme to museli riešiť takto:

def zisti(znak, retazec):
    for z in retazec:
        if z == znak:
            return True
    return False
>>> zisti('y', 'Python')
True
>>> zisti('T', 'Python')
False

Pritom existuje binárna operácia in, ktorá zisťuje, či sa zadaný podreťazec nachádza v nejakom konkrétnom reťazci. Jej tvar je

podretazec in retazec

Najčastejšie sa bude využívať v príkaze if a v cykle while, napr.

>>> 'nt' in 'Monty Python'
True
>>> 'y P' in 'Monty Python'
True
>>> 'tyPy' in 'Monty Python'
False
>>> 'pyt' in 'Monty Python'
False

Na rozdiel od našej vlastnej funkcie zisti(), operácia in funguje nielen pre zisťovanie jedného znaku, ale aj pre ľubovoľne dlhý podreťazec.

Ak niekedy budeme potrebovať negáciu tejto podmienky, môžeme zapísať:

if not 'a' in retazec:
    ...
if 'a' not in retazec:
    ...

Pričom sa odporúča druhý spôsob zápisu not in.

Operácia indexovania [ ]

Pomocou tejto operácie vieme pristupovať k jednotlivým znakom postupnosti (znakový reťazec je postupnosť znakov). Jej tvar je

reťazec[číslo]

Celému číslu v hranatých zátvorkách hovoríme index:

  • znaky v reťazci sú indexované od 0 do len()-1, t.j. prvý znak v reťazci má index 0, druhý 1, … posledný má index len()-1

  • výsledkom indexovania je vždy 1-znakový reťazec (čo je nový reťazec s kópiou 1 znaku z pôvodného reťazca) alebo chybová správa, keď indexujeme mimo znaky reťazca

Očíslujme znaky reťazca:

indexy v reťazci

M

o

n

t

y

P

y

t

h

o

n

0

1

2

3

4

5

6

7

8

9

10

11

Napr. do premennej abc priradíme reťazec 12 znakov a pristupujeme ku niektorým znakom pomocou indexu:

>>> abc = 'Monty Python'
>>> abc[3]
't'
>>> abc[9]
'h'
>>> abc[12]
...
IndexError: string index out of range
>>> abc[len(abc)-1]
'n'

Vidíme, že posledný znak v reťazci má index dĺžka reťazca-1. Ak indexujeme väčším číslom ako 11, vyvolá sa chybová správa IndexError: string index out of range.

Často sa indexuje v cykle, kde premenná cyklu nadobúda správneho správne hodnoty indexov, napr.

>>> a = 'Python'
>>> for i in range(len(a)):
        print(i, a[i])

0 P
1 y
2 t
3 h
4 o
5 n

Funkcia range(len(a)) zabezpečí, že cyklus prejde postupne pre všetky i od 0 do len(a)-1.

Indexovanie so zápornými indexmi

Keďže často potrebujeme pristupovať ku znakom na konci reťazca, môžeme to zapisovať pomocou záporných indexov:

abc[-5] == abc[len(abc)-5]

Znaky reťazca sú indexované od -1 do -len() takto:

záporné indexy

M

o

n

t

y

P

y

t

h

o

n

0

1

2

3

4

5

6

7

8

9

10

11

-12

-11

-10

-9

-8

-7

-6

-5

-4

-3

-2

-1

Napríklad:

>>> abc = 'Monty Python'
>>> abc[len(abc)-1]
'n'
>>> abc[-1]
'n'
>>> abc[-7]
' '
>>> abc[-13]
...
IndexError: string index out of range

alebo aj for-cyklom:

>>> a = 'Python'
>>> for i in range(1, len(a)+1):
        print(-i, a[-i])

-1 n
-2 o
-3 h
-4 t
-5 y
-6 P

alebo for-cyklom so záporným krokom:

>>> a = 'Python'
>>> for i in range(-1, -len(a)-1, -1):
        print(i, a[i])

-1 n
-2 o
-3 h
-4 t
-5 y
-6 P

Podreťazce

Indexovať môžeme nielen jeden znak, ale aj nejaký podreťazec celého reťazca. Opäť použijeme operátor indexovania, ale index bude obsahovať znak ':':

reťazec[prvý : zaposledný]

kde

  • prvý je index začiatku podreťazca

  • zaposledný je index prvku jeden za, t.j. musíme písať index prvku o 1 viac

  • takejto operácii hovoríme rez (alebo po anglicky slice)

  • ak takto indexujeme mimo reťazec, nenastane chyba, ale prvky mimo sú prázdny reťazec

Ak indexujeme rez od 6. po 11. prvok:

podreťazec

M

o

n

t

y

P

y

t

h

o

n

^

^

0

1

2

3

4

5

6

7

8

9

10

11

prvok s indexom 11 už vo výsledku nebude:

>>> abc = 'Monty Python'
>>> abc[6:11]
'Pytho'
>>> abc[6:12]
'Python'
>>> abc[6:len(abc)]
'Python'
>>> abc[6:12]
'Python'
>>> abc[10:16]
'on'

Podreťazce môžeme vytvárať aj v cykle:

>>> a = 'Python'
>>> for i in range(len(a)):
        print(f'{i}:{i+3} {a[i:i+3]}')

0:3 Pyt
1:4 yth
2:5 tho
3:6 hon
4:7 on
5:8 n

alebo

>>> a = 'Python'
>>> for i in range(len(a)):
        print(f'{i}:{len(a)} {a[i:len(a)]}')

0:6 Python
1:6 ython
2:6 thon
3:6 hon
4:6 on
5:6 n

Ešte otestujte aj takéto zápisy:

>>> 'Python'[1:-1]
'ytho'
>>> 'Python'[-5:4]
'yth'
>>> 'Python'[-3:3]
''
>>> 'Python'[1:-1][1:-1]
'th'

Dobre sa zamyslite nad každým z týchto reťazcových výrazov.

Predvolená hodnota

Ak neuvedieme prvý index v podreťazci, bude to označovať rez od začiatku reťazca. Zápis je takýto:

reťazec[ : zaposledný]

Ak neuvedieme druhý index v podreťazci, označuje to, že chceme rez až do konca reťazca. Teda:

reťazec[prvý : ]

Uvedomte si, že zápisy reťazec[:počet] a reťazec[-počet:] budú veľmi často označovať buď prvých alebo posledných počet znakov reťazca.

Ak neuvedieme ani jeden index v podreťazci, označuje to, že chceme celý reťazec, t.j. vytvorí sa kópia pôvodného reťazca:

reťazec[ : ]
kladné aj záporné indexy

M

o

n

t

y

P

y

t

h

o

n

0

1

2

3

4

5

6

7

8

9

10

11

-12

-11

-10

-9

-8

-7

-6

-5

-4

-3

-2

-1

napríklad

>>> abc = 'Monty Python'
>>> abc[6:]                  # od 6. znaku do konca
'Python'
>>> abc[:5]                  # od zaciatku po 4. znak = prvých 5 znakov reťazca
'Monty'
>>> abc[-4:]                 # od 4. od konca az do konca = posledné 4 znaky reťazca
'thon'
>>> abc[16:]                 # indexujeme mimo retazca
''

Podreťazce s krokom

Podobne ako vo funkcii range() aj pri indexoch podreťazca môžeme určiť aj krok indexov:

reťazec[prvý : zaposledný : krok]

kde krok určuje o koľko sa bude index v reťazci posúvať od prvý po posledný. Napríklad:

>>> abc = 'Monty Python'
>>> abc[2:10:2]
'nyPt'
>>> abc[::3]
'MtPh'
>>> abc[9:-7:-1]
'htyP'
>>> abc[::-1]
'nohtyP ytnoM'
>>> abc[6:] + ' ' + abc[:5]
'Python Monty'
>>> abc[4::-1] + ' ' + abc[:5:-1]
'ytnoM nohtyP'
>>> (abc[6:] + ' ' + abc[:5])[::-1]
'ytnoM nohtyP'
>>> 'kobyla ma maly bok'[::-1]
'kob ylam am alybok'
>>> abc[4:9]
'y Pyt'
>>> abc[4:9][2]          # aj podretazce mozeme dalej indexovat
'P'
>>> abc[4:9][2:4]
'Py'
>>> abc[4:9][::-1]
'tyP y'

Reťazce sú v pamäti nemenné (nemeniteľné)

Typ str (znakové reťazce) je nemeniteľný typ (hovoríme aj immutable). To znamená, že hodnota reťazca sa v pamäti nedá zmeniť. Ak budeme potrebovať reťazec, v ktorom je nejaká zmena, budeme musieť skonštruovať nový. Napr.

>>> abc[6] = 'K'
TypeError: 'str' object does not support item assignment

Všetky doterajšie manipulácie s reťazcami nemenili reťazec, ale zakaždým vytvárali úplne nový (niekedy to bola len kópia pôvodného), napr.

>>> cba = abc[::-1]
>>> abc
'Monty Python'
>>> cba
'nohtyP ytnoM'

Takže, keď chceme v reťazci zmeniť nejaký znak, budeme musieť skonštruovať nový reťazec, napr. takto:

>>> abc[6] = 'K'
...
TypeError: 'str' object does not support item assignment
>>> novy = abc[:6] + 'K' + abc[7:]
>>> novy
'Monty Kython'
>>> abc
'Monty Python'

Alebo, ak chceme opraviť prvý aj posledný znak:

>>> abc = 'm' + abc[1:-1] + 'N'
>>> abc
'monty PythoN'

Porovnávanie jednoznakových reťazcov

Jednoznakové reťazce môžeme porovnávať relačnými operátormi ==, !=, <, <=, >, >=, napr.

>>> 'x' == 'x'
True
>>> 'm' != 'M'
True
>>> 'a' > 'm'
False
>>> 'a' > 'A'
True

Python na porovnávanie používa vnútornú reprezentáciu Unicode (UTF-8). S touto reprezentáciou môžeme pracovať pomocou funkcií ord() a chr():

  • funkcia ord(znak) vráti vnútornú reprezentáciu znaku (kódovanie v pamäti počítača)

    >>> ord('a')
    97
    >>> ord('A')
    65
    
  • opačná funkcia chr(číslo) vráti jednoznakový reťazec, pre ktorý má tento znak danú číselnú reprezentáciu

    >>> chr(66)
    'B'
    >>> chr(244)
    'ô'
    

Pri porovnávaní dvoch znakov sa porovnávajú ich vnútorné reprezentácie, t.j.

>>> ord('a') > ord('A')
True
>>> 97 > 65
True
>>> 'a' > 'A'
True

Vnútornú reprezentáciu niektorých znakov môžeme zistiť napr. pomocou for-cyklu:

>>> for i in range(ord('A'), ord('J')):
        print(i, chr(i))

65 A
66 B
67 C
68 D
69 E
70 F
71 G
72 H
73 I

Vyskúšajte:

>>> chr(8984)
>>> chr(9819)
>>> chr(9860)
>>> chr(9328)

Prípadne vyskúšajte vypísať tieto znaky do grafickej plochy pomocou nejakého väčšieho fontu v create_text.

Porovnávanie dlhších reťazcov

Dlhšie reťazce Python porovnáva postupne po znakoch:

  • kým sú v oboch reťazcoch rovnaké znaky, preskakuje ich

  • pri prvom rôznom znaku, porovná tieto dva znaky

Napr. pri porovnávaní dvoch reťazcov ‚kocur‘ a ‚kohut‘:

  • porovná 0. znaky: 'k' == 'k'

  • porovná 1. znaky: 'o' == 'o'

  • porovná 2. znaky: 'c' <  'h' a tu aj skončí porovnávanie týchto reťazcov

Preto platí, že 'kocur' < 'kohut'. Treba si dávať pozor na znaky s diakritikou, lebo, napr. ord('č') = 269 > ord('h') = 104. Napr.

>>> 'kocúr' < 'kohút'
True
>>> 'kočka' < 'kohut
False
>>> 'PYTHON' < 'Python' < 'python'
True

alebo

>>> 'pytagoras' < 'python' < 'pytliak' < 'pyton'
True

Prechádzanie reťazca v cykle

Už sme videli, že prvky znakového reťazca môžeme prechádzať for-cyklom, v ktorom indexujeme celý reťazec postupne od 0 do len()-1:

>>> a = 'Python'
>>> for i in range(len(a)):
        print('.' * i, a[i])

 P
. y
.. t
... h
.... o
..... n

Tiež vieme, že for-cyklom môžeme prechádzať nielen postupnosť indexov (t.j. range(len(a))), ale priamo postupnosť znakov, napr.

>>> for znak in 'python':
        print(znak * 5)

ppppp
yyyyy
ttttt
hhhhh
ooooo
nnnnn

Zrejme reťazec vieme prechádzať aj while-cyklom, napr.

a = '.....veľa bodiek'
print(a)
while len(a) != 0 and a[0] == '.':
    a = a[1:]
print(a)
.....veľa bodiek
veľa bodiek

Cyklus sa opakoval, kým bol reťazec neprázdny a kým boli na začiatku reťazca znaky bodky '.'. Vtedy sa v tele cyklu reťazec skracoval o prvý znak. Uvedený while-cyklus môžeme zapísať aj takto:

while a and a[0] == '.':
    a = a[1:]

Zamyslite sa, prečo musíme v podmienke kontrolovať, či je reťazec neprázdny. Hoci v tomto prípade by sme to vedeli zapísať aj takto (menej prehľadne):

while a[0:1] == '.':
    a = a[1:]

Automatické číslovanie prechodov vo for-cykle

Už sme si zvykli, že ak by sme potrebovali v takomto cykle:

for znak in 'Python':
    print(znak)

ku každému vypisovanému znaku pridať aj jeho poradové číslo, musíme použiť pomocnú premennú a zyšovať ju v cykle o 1, napr. takto:

i = 0
for znak in 'Python':
    print(i, znak)
    i += 1

Keďže teraz už vieme znakové reťazce aj indexovať, vieme to prepísať aj takto:

retazec = 'Python'
for i in range(len(retazec)):
    print(i, retazec[i])

Existuje ešte jeden spôsob, ktorý je z týchto „najpythonovejší“ (pythonic). Využijeme ďalšiu štandardnú funkciu enumerate, pomocou ktorej môžeme for-cyklus zapísať novým štýlom:

for i, znak in enumerate('Python'):
    print(i, znak)

Táto funkcia očakáva, že ako parameter dostane nejakú postupnosť (napr. postupnosť znakov) a jej úlohou bude každý prvok tejto postupnosti očíslovať. Vďaka tomuto for-cyklus dokáže nastavovať naraz dve premenné cyklu: premennú pre očíslovanie (premenná i) a premennú pre hodnotu (u nás je to premenná znak). Tento cyklus teda prejde 6-krát, ale zakaždým nastaví naraz dve premenné:

0 P
1 y
2 t
3 h
4 o
5 n

Všimnite si, že číslovanie prechodov cyklu prebieha od 0 a nie od 1, presne tak, ako je to v Pythone zvykom.

Ukážme ešte jeden príklad s enumerate. Najprv obyčajný for-cyklus:

for cislo in range(5, 25, 6):
    print(cislo)
5
11
17
23

Ak teraz potrebujeme očíslovať každý prechod tohto for-cyklu, môžeme zapísať:

for i, cislo in enumerate(range(5, 25, 6)):
    print(i, cislo)
0 5
1 11
2 17
3 23

Reťazcové funkcie

Už poznáme tieto štandardné funkcie:

  • len() - dĺžka reťazca

  • int(), float() - prevod reťazca na celé alebo desatinné číslo

  • bool() - prevod reťazca na True alebo False (ak je prázdny, výsledok bude False)

  • str() - prevod čísla (aj ľubovoľnej inej hodnoty) na reťazec

  • ord(), chr() - prevod do a z Unicode

Okrem nich existujú ešte aj tieto tri užitočné štandardné funkcie:

  • bin() - prevod celého čísla do reťazca, ktorý reprezentuje toto číslo v dvojkovej sústave

  • hex() prevod celého čísla do reťazca, ktorý reprezentuje toto číslo v šestnástkovej sústave

  • oct() - prevod celého čísla do reťazca, ktorý reprezentuje toto číslo v osmičkovej sústave

Napríklad

>>> bin(123)
'0b1111011'
>>> hex(123)
'0x7b'
>>> oct(123)
'0o173'

Zápisy celého čísla v niektorej z týchto sústav fungujú ako celočíselné konštanty:

>>> 0b1111011
123
>>> 0x7b
123
>>> 0o173
123

Vlastné funkcie

Môžeme vytvárať vlastné funkcie, ktoré majú aj reťazcové parametre, resp. môžu vracať reťazcovú návratovú hodnotu. Niekoľko námetov:

  • funkcia vráti True ak je daný znak (jednoznakový reťazec) číslicou:

    def je_cifra(znak):
        return '0' <= znak <= '9'
    

    alebo inak

    def je_cifra(znak):
        return znak in '0123456789'
    
  • funkcia vráti True ak je daný znak (jednoznakový reťazec) malé alebo veľké písmeno (anglickej abecedy)

    def je_pismeno(znak):
        return 'a' <= znak <= 'z' or 'A' <= znak <= 'Z'
    
  • parametrom funkcie je reťazec s menom a priezviskom (oddelené sú práve jednou medzerou) - funkcia vráti reťazec, v ktorom bude najprv priezvisko a až za tým meno (oddelené medzerou)

    def meno(r):
        ix = 0
        while ix < len(r) and r[ix] != ' ':     # najde medzeru
            ix += 1
        return r[ix+1:] + ' ' + r[:ix]
    
  • funkcia vráti prvé slovo vo vete; predpokladáme, že obsahuje len malé a veľké písmená (využijeme funkciu je_pismeno)

    def slovo(veta):
        for i in range(len(veta)):
            if not je_pismeno(veta[i]):
                return veta[:i]
        return veta
    

Reťazcové metódy

Je to špeciálny spôsob zápisu volania funkcie (bodková notácia):

reťazec.metóda(parametre)

kde metóda je meno niektorej z metód, ktoré sú v systéme už definované pre znakové reťazce. My si ukážeme niekoľko užitočných metód, s niektorými ďalšími sa zoznámime neskôr:

  • reťazec.count(podreťazec) - zistí počet výskytov podreťazca v reťazci

  • reťazec.find(podreťazec) - zistí index prvého výskytu podreťazca v reťazci

  • reťazec.lower() - vráti reťazec, v ktorom prevedie všetky písmená na malé

  • retazec.upper() - vráti reťazec, v ktorom prevedie všetky písmená na veľké

  • reťazec.replace(podreťazec1, podreťazec2) - vráti reťazec, v ktorom nahradí všetky výskyty podreťazec1 iným reťazcom podreťazec2

  • reťazec.strip() - vráti reťazec, v ktorom odstráni medzery na začiatku a na konci reťazca (odfiltruje pritom aj iné oddeľovacie znaky ako '\n' a '\t')

  • reťazec.format(hodnoty) - vráti reťazec, v ktorom nahradí formátovacie prvky '{}' zadanými hodnotami

Ak chceme o niektorej z metód získať help, môžeme zadať, napr.

>>> help(''.find)
Help on built-in function find:

find(...) method of builtins.str instance
    S.find(sub[, start[, end]]) -> int

    ...

metóda reťazec.count()

reťazec.count(podreťazec)
Parametre
  • reťazec – reťazec, v ktorom sa budú hľadať všetky výskyty nejakého zadaného podreťazca

  • podreťazec – hľadaný podreťazec

Metóda zistí počet všetkých výskytov podreťazca v danom reťazci. Napr.

>>> 'Python'.count('th')
1         # reťazec 'th' sa nachádza v 'Python' iba raz
>>> 'Python'.count('to')
0         # reťazec 'to' sa v 'Python' nenachádza ani raz
>>> 'Pyp ypY Ypy yPy yPY'.count('Py')
2         # reťazec 'Py' sa tu nachádza na 2 miestach

metóda reťazec.find()

reťazec.find(podreťazec)
Parametre
  • reťazec – reťazec, v ktorom sa budú hľadať prvý výskyt nejakého zadaného podreťazca

  • podreťazec – hľadaný podreťazec

Metóda nájde prvý najľavejší výskyt podreťazca v danom reťazci alebo vráti -1, keď ho nenájde. Napr.

>>> 'Python'.find('th')
2                          # retazec 'th' sa nachadza v 'Python' od 2. indexu
>>> 'Python'.find('to')
-1                         # retazec 'to' sa v 'Python' nenachadza
>>> 'abcd ce abced'.find('c')
5                          # prvy vyskyt retazca 'ce' je na indexe 5

metóda reťazec.lower()

reťazec.lower()
Parametre

reťazec – reťazec, z ktorého sa vyrobí nový ale s malými písmenami

Metóda vyrobí kópiu daného reťazca, v ktorej všetky veľké písmená prerobí na malé. Nepísmenové znaky nemení. Napr.

>>> 'PyTHon'.lower()
'python'
>>> '1+2'.lower()
'1+2'

metóda reťazec.upper()

reťazec.upper()
Parametre

reťazec – reťazec, z ktorého sa vyrobí nový ale s veľkými písmenami

Metóda vyrobí kópiu daného reťazca, v ktorej všetky malé písmená prerobí na veľké. Nepísmenové znaky nemení. Napr.

>>> 'PyTHon'.upper()
'PYTHON'
>>> '1+2'.upper()
'1+2'

metóda reťazec.replace(podreťazec1, podreťazec2)

reťazec.replace()
Parametre

reťazec – reťazec, z ktorého sa vyrobí nový ale s nahradenými výskytmi podreťazcov

Metóda vyrobí kópiu daného reťazca, v ktorej všetky veľké výskyty podreťazec1 prerobí na podreťazec2. Napr.

>>> 'Monty Python'.replace('y', '***')
'Mont*** P***thon'
>>> 'abradabra'.replace('ra', 'y').replace('by', 'ko')
'akodako'

Najprv sa nahradia výskyty 'ra' na jednoznakové 'y' (dostaneme 'abydaby') a v tomto novom reťazci sa výskyty 'by' nahradia reťazcom 'ko'. Všimnite si, že bude fungovať aj:

>>> (10 * 'abc').replace('bc', '=')
'a=a=a=a=a=a=a=a=a=a='

metóda reťazec.strip()

reťazec.strip()
Parametre

reťazec – reťazec, z odfiltruje medzerové znaky na začiatku a na konci reťazca

Metóda vyrobí kópiu daného reťazca, v ktorej vyhodí všetky medzerové znaky (medzery ale aj '\n' a '\t') zo začiatku aj konca reťazca. Napr.

>>> '          Python'.strip()
'Python'
>>> 'Python\n\n\n'.strip()
'Python'
>>> 'P y  t h  o n'.strip()
'P y  t h  o n'

Formátovanie reťazca

Možnosti formátovania pomocou formátovacích reťazcov f'{x}' sme už videli predtým. Teraz ukážeme niekoľko užitočných formátovacích prvkov. Volanie má tvar:

f'formátovací reťazec s hodnotami v {}'

Takýto zápis ale funguje až od verzie Pythonu 3.6 a vyššie. V starších verziách budeme musieť použiť tvar reťazec.format(parametre), ale ten tu rozoberať nebudeme, hoci sa veľmi podobá novšiemu zápisu.

Formátovací reťazec obsahuje formátovacie prvky, ktoré sa nachádzajú v {} zátvorkách: v týchto zátvorkách sa musí náchadzať priamo nejaká hodnota (môže to byť ľubovoľný pythonovský výraz) a za ňou sa môže nachádzať špecifikácia oddelená znakom ':'. Napr.

>>> x = 1237
>>> a = f'vysledok pre x={x} je {x + 8}'
>>> ahoj = 'hello'
>>> b = f'ahoj po anglicky je "{ahoj}"'

Python v tomto prípade najprv vyhľadá všetky výskyty zodpovedajúcich '{}' a vyhodnotí (vypočíta hodnoty) výrazov, ktoré sú vo vnútri týchto zátvoriek. Tieto hodnoty potom dosadí do reťazca namiesto zápisu {...}. Zrejme, ak to neboli reťazcové hodnoty (napr. to boli čísla), tak ich najprv prevedie na reťazce.

V ďalšom príklade sme v {} použili aj nejaké špecifikácie, t.j. upresnenie formátovania:

>>> r, g, b = 100, 150, 200
>>> farba = f'#{r:02x}{g:02x}{b:02x}'

Špecifikácia formátu

V zátvorkách '{}' sa môžu nachádzať rôzne upresnenia formátovania, ktorými určujeme detaily, ako sa budú vypočítané hodnoty prevádzať na reťazce. Prvé číslo za : váčšinou označuje šírku (počet znakov), do ktorej sa vloží reťazec a ďalej tam môžu byť znaky na zarovnanie ('<', '>', '^') a znaky na typ hodnoty ('d', 'f', …). Napr.:

  • '{hodnota:10}' - šírka výpisu 10 znakov

  • '{hodnota:>7}' - šírka 7, zarovnané vpravo

  • '{hodnota:<5d}' - šírka 5, zarovnané vľavo, parameter musí byť celé číslo (bude sa vypisovať v 10-ovej sústave)

  • '{hodnota:12.4f}' - šírka 12, parameter desatinné číslo vypisované na 4 desatinné miesta

  • '{hodnota:06x}' - šírka 6, zľava doplnená nulami, parameter celé číslo sa vypíše v 16-ovej sústave

  • '{hodnota:^20s}' - šírka 20, vycentrované, parametrom je reťazec

Zhrňme najpoužívanejšie písmená pri označovaní typu parametra:

  • d - celé číslo v desiatkovej sústave

  • b - celé číslo v dvojkovej sústave

  • x - celé číslo v šestnástkovej sústave

  • s - znakový reťazec

  • f - desatinné číslo (možno špecifikovať počet desatinných miest, inak default 6)

  • g - desatinné číslo vo všeobecnom formáte

Dokumentačný reťazec pri definovaní funkcie

Ak funkcia vo svojom tele hneď ako prvý riadok obsahuje znakový reťazec (zvykne byť viacriadkový s '''), tento sa stáva, tzv. dokumentačným reťazcom (docstring). Pri vykonávaní tela funkcie sa takéto reťazce ignorujú (preskakujú). Tento reťazec (docstring) sa ale môže neskôr vypísať, napr. štandardnou funkciou help().

Zadefinujme reťazcovú funkciu a hneď do nej dopíšeme aj niektoré základné informácie:

def pocet_vyskytov(podretazec, retazec):
    '''funkcia vráti počet výskytov podreťazca v reťazci

    prvý parameter podretazec - ľubovoľný neprázdny reťazec, o ktorom sa
                                bude zisťovať počet výskytov
    druhý parameter retazec - reťazec, v ktorom sa hľadajú výskyty

    ak je prvý parameter podretazec prázdny reťazec, funkcia vráti len(retazec)
    '''
    pocet = 0
    for ix in range(len(retazec)):
        if retazec[ix:ix+len(podretazec)] == podretazec:
            pocet += 1
    return pocet

Takto definovaná funkcia funguje rovnako, ako keby žiaden dokumentačný reťazec neobsahovala, ale teraz bude fungovať aj:

>>> help(pocet_vyskytov)
Help on function pocet_vyskytov in module __main__:

pocet_vyskytov(podretazec, retazec)
    funkcia vráti počet výskytov podreťazca v reťazci

    prvý parameter podretazec - ľubovoľný neprázdny reťazec, o ktorom sa
                                bude zisťovať počet výskytov
    druhý parameter retazec - reťazec, v ktorom sa hľadajú výskyty

    ak je prvý parameter podretazec prázdny reťazec, funkcia vráti len(retazec)

Tu môžeme vidieť užitočnú vlastnosť Pythonu: programátor, ktorý vytvára nejaké nové funkcie, môže hneď vytvárať aj malú dokumentáciu o jej používaní pre ďalších programátorov. Asi ľahko uhádneme, ako funguje napr. aj toto:

>>> help(hex)
Help on built-in function hex in module builtins:

hex(number, /)
    Return the hexadecimal representation of an integer.

    >>> hex(12648430)
    '0xc0ffee'

Pri takomto spôsobe samodokumentácie funkcií si treba uvedomiť, že Python v tele funkcie ignoruje nielen všetky reťazce, ale aj iné konštanty:

  • ak napr. zavoláme funkciu, ktorá vracia nejakú hodnotu a túto hodnotu ďalej nespracujeme (napr. priradením do premennej, použitím ako parametra inej funkcie, …), vyhodnocovanie funkcie takúto návratovú hodnotu ignoruje

  • ak si uvedomíme, že meno funkcie bez okrúhlych zátvoriek nespôsobí volanie tejto funkcie, ale len hodnotu referencie na funkciu, tak aj takýto zápis sa ignoruje

Napr. všetky tieto zápisy sa v tele funkcie (alebo aj v programovom režime mimo funkcie) ignorujú:

s.replace('a', 'b')
print
g.pack
pocet + 1
i == i + 1
math.sin(uhol)

Python pri nich nehlási ani žiadnu chybu.

Príklad s kreslením a reťazcami

Navrhnime malú aplikáciu, v ktorej budeme pohybovať mysleným perom. Toto pero sa bude hýbať v jednom zo štyroch smerov: 's' pre sever, 'v' pre východ, 'j' pre juh, 'z' pre západ. Dĺžka kroku pera nech je nejaká malá konštanta, napr. 10:

import tkinter

def kresli(retazec):
    x, y = 100, 100
    for znak in retazec:
        x1, y1 = x, y
        if znak == 's':
            y1 -= 10
        elif znak == 'v':
            x1 += 10
        elif znak == 'j':
            y1 += 10
        elif znak == 'z':
            x1 -= 10
        else:
            print('nerozumiem "' + znak + '"')
            return
        canvas.create_line(x, y, x1, y1)
        x, y = x1, y1

canvas = tkinter.Canvas()
canvas.pack()

kresli('ssvvjjzz')

Po spustení dostaneme:

_images/06_01.png

Zrejme rôzne reťazce znakov, ktoré obsahujú len naše štyri písmená pre smery pohybu, budú kresliť rôzne útvary. Napr.

kresli('vvvvvvvjjjjjjjzzzzzzzsssssss')

nakreslí trochu väčší štvorec. Toto vieme zapísať napr. aj takto:

kresli('v'*7 + 'j'*7 + 'z'*7 + 's'*7)

Alebo

def stvorec(n):
    return 'v'*n + 'j'*n + 'z'*n + 's'*n

kresli(stvorec(7))

Na cvičeniach budeme rôzne vylepšovať túto ideu funkcie kresli()



Cvičenia

L.I.S.T.

  1. Ručne zisti, čo sa vypíše:

    • najprv bez počítača:

      >>> x, y = 'Bratislava', 'Praha'
      >>> y[1] + x[4] + y[3] + x[-4] + y[-5]
      ...
      >>> x[5:8] + 3 * x[3] + y[2:]
      ...
      >>> y[:2] + x[-2:]
      ...
      >>> x[1::2] + y[2::2] + x[2::3]
      ...
      >>> x.replace('a', 'e') + y.replace('a', 'i')
      ...
      >>> (y + x).replace('ra', '').replace('a', 'xa')
      ...
      
    • potom to skontroluj pomocou Pythonu

  2. Napíš funkciu sucet(retazec), ktorá dostáva znakový reťazec s dvomi celými číslami oddelenými jednou medzerou. Funkcia vráti (nič nevypisuje) celé číslo, ktoré je súčtom dvoch čísel v reťazci.

    • napr.

      >>> sucet('12 9')
      21
      >>> sucet('987654321 99999')
      987754320
      
  3. Napíš funkciu dalsi(znak, o_kolko=1), ktorá pre zadaný znak vráti taký znak, ktorého kód (funkcia ord) je o zadaný o_kolko posunutý. Funkcia nič nevypisuje.

    • napr.

      >>> dalsi('A')
      'B'
      >>> dalsi('c', 2)
      'e'
      >>> dalsi('=', -3)
      ':'
      
  4. Napíš funkciu rozsekaj(text, sirka), ktorá vypíše zadaný text do viacerých riadkov (pomocou print), pričom každý (možno okrem posledného) má presne sirka znakov.

    • napr.

      >>> rozsekaj('Anicka dusicka, kde si bola', 5)
      Anick
      a dus
      icka,
       kde
      si bo
      la
      
  5. Vieme, že kód znaku pre kartové srdce je 9829. Napíš program, ktorý do grafickej plochy nakresli vedľa seba všetky 4 kartové symboly nejakým veľkým fontom a rôznymi farbami.

    • napr. takto

      _images/06_02.png
  6. Napíš funkciu z16(znak), ktorá z jedného znaku reprezentujúceho cifru v 16-ovej sústave (číslica alebo jedno z 'abcdefABCDEF') vráti zodpovedajúce číslo. Funkcia nič nevypisuje, len vráti (return) celé číslo.

    • napr.

      >>> z16('7')
      7
      >>> z16('a')
      10
      >>> z16('E')
      14
      
  7. Napíš funkciu cf16(retazec), ktorá vypočíta ciferný súčet čísla zadaného v 16-ovej sústave. Funkcia nič nevypisuje, len vráti (return) celé číslo.

    • napr.

      >>> cf16('7a0')
      17
      >>> cf16('FFFFFF')
      90
      
  8. Znakový reťazec vieme prevrátiť pomocou zápisu retazec[::-1]. Napíš funkciu prevrat(retazec), ktorá len pomocou cyklu a zreťazovania prevráti zadaný reťazec. Funkcia nič nevypisuje, jej výsledkom (return) je nový znakový reťazec.

    • napr.

      >>> prevrat('tseb eht si nohtyP')
      'Python is the best'
      
  9. Napíš funkciu strip(reťazec), ktorá urobí presne to isté ako metóda reťazec.strip(), t.j. vyhodí medzerové znaky zo začiatku aj konca reťazca. Nepoužívaj žiadnu reťazcovú metódu. Funkcia nič nevypisuje.

    • napr.

      >>> strip(' pyt   hon\n ')
      'pyt   hon'
      
  10. Napíš funkciu nahrad_samo(text, znaky), ktorá v zadanom texte nahradí všetky samohlásky ('aeiouy') zadaným reťazcom. Funkcia nič nevypisuje, ale vráti nový znakový reťazec.

    • napr.

      >>> nahrad_samo('sedi mucha na stene', 'a')
      'sada macha na stana'
      >>> nahrad_samo('sedi mucha na stene', 'uo')
      'suoduo muochuo nuo stuonuo'
      
  11. Napíš funkciu rozdel_na_slova(veta), ktorá zo zadaného znakového reťazca vypíše (pomocou print) pod seba všetky slová. Predpokladáme, že v tomto reťazci sú slová navzájom oddelené jednou medzerou.

    • napr.

      >>> rozdel_na_slova('isiel Macek do Malaciek')
      isiel
      Macek
      do
      Malaciek
      >>> rozdel_na_slova('Juraj_Janosik')
      Juraj_Janosik
      
  12. Napíš funkciu riadky(retazec), ktorá vypíše daný viacriadkový reťazec, ale pritom každý riadok očísluje číslami od 1 do počet riadkov.

    • napr.

      >>> riadky('prvy riadok\n\ntreti je posledny')
      1. prvy riadok
      2.
      3. treti je posledny
      >>> riadky('len \\n jeden riadok')
      1. len \n jeden riadok
      
  13. Napíš funkciu nahodny_znak(reťazec), ktorá z daného reťazca vráti náhodne zvolený znak. Funkcia nič nevypisuje.

    • napr.

      >>> nahodny_znak('aeiouy')
      'u'
      >>> nahodny_znak('aeiouy')
      'a'
      >>> nahodny_znak('bcdfghjklmnpqrstvwxz')
      'c'
      
  14. Napíš funkciu nahodne_slovo(počet), ktorá vygeneruje náhodné slovo, v ktorom sa strieda zadaný počet dvojíc náhodná spoluhláska a náhodná samohlaska.

    • napr.

      >>> for i in range(5):
              print(nahodne_slovo(3 + i // 3))
      
      mykumu
      wizodi
      bujapa
      gicynedo
      cejozoly
      syredocu
      syxovyqusu
      dapazisasa
      haponamehe
      
  15. Na konci prednášky je funkcia kresli(retazec), pomocou ktorej môžeme vytvárať nejakú kresbu zakódovanú písmenami 'svjz'. Nakresli pomocou tejto funkcie takýto obrázok:

    • _images/06_03.png
  16. Dopíš do tejto funkcie spracovanie týchto ďalších znakov:

    • 'h' - kresliace pero sa bude odteraz pohybovať bez kreslenia (pero hore)

    • 'd' - kresliace pero bude odteraz pri pohybe kresliť (pero dole)

    • číslice od '1' do '9' - nasledovný príkaz (jeden z 'svjz') sa vykoná príslušný počet krát

    • napr.

      >>> kresli('4v4j4z4sh5vd'*5)
      

      nakreslí vedľa seba 5 štvorcov:

      _images/06_04.png
  17. Napíš funkciu stvorce(n, m), ktorá vygeneruje sieť štvorčekov veľkosti 3 (t.j. '3v3j3z3s') - v skutočnosti vygeneruje znakový reťazec, pomocou ktorého funkcia kresli nakreslí štvorčekovú sieť. Parameter n označuje počet riadkov a m je počet stĺpcov siete.

    • napr.

      >>> kresli(stvorce(3, 4))
      

      nakreslí:

      _images/06_05.png
  18. Napíš funkciu je_palindrom(reťazec), ktorá zistí (vráti True alebo False), či je zadaný reťazec palindrom. Funkcia ignoruje medzery a nerozlišuje rozdiel medzi malými a veľkými písmenami.

    • napr.

      >>> je_palindrom('Python')
      False
      >>> je_palindrom('tahat')
      True
      >>> je_palindrom('Jelenovi Pivo Nelej')
      True
      
  19. Metóda 'reťazec'.count(podreťazec) zistí počet výskytov podreťazca v reťazci. Napíš funkciu pocet(retazec, podretazec), ktorá robí to isté, ale bez použitia tejto metódy.

    • napr.

      >>> pocet('mama ma emu a ema ma mamu', 'ma ')
      4
      >>> pocet('mama ma emu a ema ma mamu', 'am')
      2
      
  20. Napíš funkciu usporiadaj(ret1, ret2, ret3), ktorá z troch zadaných reťazcov vytvorí nový (vráti ho ako výsledok funkcie) zlepením týchto troch v utriedenom poradí: najprv prvý v abecede, potom druhý a na koniec tretí. Medzi zlepené reťazce vloží medzeru.

    • napr.

      >>> usporiadaj('python', 'pytliak', 'pytagoras')
      'pytagoras python pytliak'
      


1. Domáce zadanie

L.I.S.T.

Napíšte pythonovský skript, ktorý bude definovať tieto 4 funkcie:

  • pocet_dni_v_mesiaci(mesiac, priestupny=False) - vráti číslo od 28 do 31, mesiace číslujeme od 1 pre január do 12 pre december

    • zrejme pocet_dni_v_mesiaci(2, True) vráti 29

  • pocet_dni_medzi(mesiac1, rok1, mesiac2, rok2) - pre dva dátumy vypočíta počet dní, ktoré sú medzi nimi

    • prvý dátum je 1. mesiac1 rok1, druhý dátum je 1. mesiac2 rok2

    • môžete predpokladať, že prvý dátum je pred alebo rovný druhému dátumu, keď sa oba dátumy rovnajú, funkcia vráti 0

    • oba parametre pre roky budú v intervale <1901, 2099>

    • napr.

      >>> pocet_dni_medzi(9, 2017, 10, 2017)      # medzi 1.9.2017 a 1.10.2017
      30
      >>> pocet_dni_medzi(9, 2017, 9, 2018)       # medzi 1.9.2017 a 1.9.2018
      365
      >>> pocet_dni_medzi(1, 1999, 10, 2017)      # medzi 1.1.1999 a 1.10.2017
      6848
      
    • zrejme využijete funkciu pocet_dni_v_mesiaci() a to, že priestupný rok (rok%4==0) má 366 dní a nepriestupný 365

  • den_v_tyzdni(den, mesiac, rok) - vráti číslo od 1 do 7, ktoré označujú pondelok až nedeľu

    • môžete to počítať tak, že najprv zistíte počet dní, ktoré uplynuli od dátumu 1.1.1901 a keďže vieme, že vtedy bol utorok, ľahko z toho vypočítate deň v týždni (bude to nejaký zvyšok po delení 7)

    • môžete predpokladať, že dátum bude zadaný korektne a bude z intervalu <1.1.1901, 31.12.2099>

    • napr.

      >>> den_v_tyzdni(18, 10, 2017)
      3
      >>> den_v_tyzdni(1, 1, 1901)
      2
      >>> den_v_tyzdni(23, 6, 1912)
      7
      
    • Viete zistiť, čím je dátum 23. júna 1912 zaujímavý?

  • kalendar(mesiac, rok) - vypíše (pomocou print()) kalendár pre jeden mesiac v takomto tvare

    • napr.

      >>> kalendar(10, 2017)
      10. mesiac 2017
      po ut st st pi so ne
                         1
       2  3  4  5  6  7  8
       9 10 11 12 13 14 15
      16 17 18 19 20 21 22
      23 24 25 26 27 28 29
      30 31
      
    • prvý riadok obsahuje číslo mesiaca a rok, druhý riadok obsahuje mená dní v týždni presne v tomto tvare, ďalej nasledujú dni v mesiaci, ktoré sú naformátované presne do zodpovedajúcich stĺpcov

    • treba si dať pozor na medzery

Na otestovanie správnosti vašich funkcii môžete využiť napr. tento štandardný modul:

>>> import calendar
>>> calendar.prmonth(2017, 10)
    October 2017
Mo Tu We Th Fr Sa Su
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

Váš odovzdaný program s menom riesenie1.py musí začínať tromi riadkami komentárov (zmeňte na vaše meno a dátum odovzdania):

# 1. zadanie: kalendár
# autor: Janko Hraško
# datum: 18.10.2018

V programe používajte len konštrukcie jazyka Python, ktoré sme sa učili na prvých 6 prednáškach. Nepoužívajte príkaz import ani indexovanie pomocou hranatých zátvoriek [ ].

Súbor riesenie1.py odovzdávajte na úlohový server https://list.fmph.uniba.sk/ najneskôr do 23:00 6. novembra, kde ho môžete nechať otestovať. Odovzdať projekt a aj ho testovať môžete ľubovoľný počet krát. Môžete zaň získať 10 bodov.