17. Premenné typu znakové reťazce, operácie, prevody

S pojmom znakový reťazec sa stretáme od prvých hodín s Pythonom. Najprv to bol vypisovaný text v print:

>>> print('Pozdravujem vas, lesy, hory!')
Pozdravujem vas, lesy, hory!

Potom to bolo meno farby pri kreslení obdĺžnikov, textov a elíps:

platno.create_rectangle(30, 150, 130, 250, width=6, fill='red')

Neskôr sa objavili znakové reťazce aj pri vypisovaní textov do grafickej plochy aj pri určovaní písma takéhoto textu:

platno.create_text(190, 40, text='Programovanie', font='Arial 40', fill='blue')

Potom sme videli znakové reťazce aj ako parametre podprogramov:

def vypis(co):
    print('*** mam rad', co, '***')

vypis('matfyz')

def vypis_python(farba):
    platno.create_text(150, 120, text='Python', fill=farba, font='times 30')

vypis_python('red')

Pozrime sa na tento dátový typ trochu podrobnejšie.

Znakové reťazce

Už vieme, že znakový reťazec je nejaký text uzavretý v apostrofoch. Niekedy môžete namiesto apostrofov vidieť úvodzovky. Python akceptuje aj úvodzovky. Väčšinou je to vecou zvyku.

Znakový reťazec môžeme priradiť do premennej rovnako, ako sme to robili s číslami. Napr.

rok = 2018
den = 'streda'
text = 'Išiel Macek do Malacek'

V pamäti sa vytvorili tri nové premenné. Mohli by sme ich zakresliť takto:

_images/17_01.png

Zrejme Python si pri každej premennej pamätá aj jej typ. To znamená, že premenná rok je celé číslo (po anglicky integer) a premenné den a text sú znakové reťazce (po anglicky string). Momentálny typ nejakej hodnoty (alebo premennej) vieme zistiť pomocou špeciálnej funkcie type. Napr.

>>> type(rok)
<class 'int'>
>>> type(den)
<class 'str'>
>>> type(text)
<class 'str'>

V týchto výpisoch sa objavilo 'int' a 'str', tieto označujú celočíselný typ (integer) a typ znakový reťazec (string). Všimnite si ešte:

>>> 22 / 7
3.142857142857143
>>> type(22 / 7)
<class 'float'>

V Pythone takéto delenie dvoch čísel vráti desatinné číslo a takýto typ má meno float.

Čítanie zo vstupu

Python má jeden veľmi dôležitý príkaz input, pomocou ktorého môže bežiaci program získať od používateľa nejaké hodnoty. Najčastejšie sa tento príkaz používa v takomto priraďovacom príkaze:

odpoved = input('nejaký text? ')

Tento príkaz najprv vypíše zadaný text a potom Python od nás čaká, že zadáme ľubovoľný text na klavesnici a stlačíme kláves <Enter>. Vtedy sa nami zadaný text priradí do príslušnej premennej, ktorú sme uviedli na ľavej strane priraďovacieho príkazu.

Napíšme takýto malý testovací program:

print('Ahoj, ja som Python.')
kto_si = input('A ty sa ako volas? ')
print('ahoj', kto_si)

Po spustení tohto programu prebehne približne takýto dialóg:

Ahoj, ja som Python.
A ty sa ako volas? Adam
ahoj Adam

Program najprv pomocou print vypíše Ahoj, ja som Python.. Do ďalšieho riadku príkaz input vypíše text A ty sa ako volas? a ďalej čaká, že na klávesnici napíšeme ľubovoľný text a ukončíme ho klávesom <Enter>. My sme tu zadali slovo Adam a preto sa do nasledovného riadku vypísal text ahoj Adam.

Ďalší program ukazuje, ako môžeme využiť čítanie zo vstupu v grafike:

import tkinter

farba = input('zadaj farbu podkladu: ')
vypisat = input('zadaj vypisovany text: ')

platno = tkinter.Canvas(bg=farba)
platno.pack()

platno.create_text(180, 120, text=vypisat, font='arial 50')

Po spustení je najprv tento dialóg:

zadaj farbu podkladu: light blue
zadaj vypisovany text: Bratislava

Potom sa do grafickej plochy nakreslí:

_images/17_02.png

V tomto príklade nám premenné so znakovými reťazcami poslúžili na nastavovanie nejakých parametrov v grafických príkazoch.

Znakové reťazce vo for-cykloch

Pripomeňme si for-cyklus, v ktorom sme vymenovali všetky hodnoty, ktoré sa postupne priradia do premennej cyklu:

for co in 7, 11, 37, 42:
    print('mam rad', co)
mam rad 7
mam rad 11
mam rad 37
mam rad 42

Lenže znakové reťazce sa môžu nachádzať aj medzi vymenovanými hodnotami, napr.

for co in 7, 11, 'matfyz', 'kremes':
    print('mam rad', co)
mam rad 7
mam rad 11
mam rad matfyz
mam rad kremes

Niekedy môžeme využiť takýto for-cyklus s reťazcami aj pri kreslení do grafickej plochy:

import tkinter

platno = tkinter.Canvas(bg='white')
platno.pack()

x = 50
y = 50
for farba in 'pink', 'plum', 'violet', 'orchid', 'magenta', 'dark magenta':
    platno.create_rectangle(x, y, x+150, y+70, fill=farba)
    platno.create_text(x+75, y+10, text=farba, font='courier 14')
    x = x + 30
    y = y + 25

Vidíme, že premenná cyklu farba tu postupne nadobúda jednu z vymenovaných hodnôt:

_images/17_03.png

Znakové reťazce v spojitosti s for-cyklom majú ešte jednu špeciálnu vlastnosť: keď namiesto vymenovanej postupnosti hodnôt pre premennú cyklu uvedieme len jeden reťazec, tak Python to pochopí tak, že tento reťazec je vymenovanou postupnosťou znakov tohto reťazca. Možeme si to predstaviť tak, že keď zapíšeme:

for i in 'AHOJ':
    ...

tak Python to rozoberie na znaky a urobí z toho cyklus:

for i in 'A', 'H', 'O', 'J':
    ...

Môžeme to otestovať napr. takto:

for znak in 'Python':
    print('***', znak, '***')
*** P ***
*** y ***
*** t ***
*** h ***
*** o ***
*** n ***

Operácie s reťazcami

Python má niekoľko operácií, ktoré pracujú so znakovými reťazcami. Napr. pre čísla funguje operácia +, ktorá označuje súčet dvoch čísel. Zrejme 12 + 34 je súčet dvoch celých čísel a výsledkom je tretie číslo 46.

Operácia zreťazenia

Jednou z najčastejšie používanýchu operácií s reťazcami je tzv. zreťazenie. Pomocou tejto operácie vznikne z dvoch reťazcov jeden nový, ktorý je zlepením dvoch pôvodných. Na túto operáciu sa používa rovnaké znamienko ako pre súčet čísel, t.j. +.

Môžeme vyskúšať:

>>> a = 'Juraj'
>>> b = 'Jánošík'
>>> a + b
'JurajJánošík'
>>> b + a
'JánošíkJuraj'
>>> a + ' ' + b
'Juraj Jánošík'

Zreťazenie sa môže využiť aj vo for-cykle takto:

ret = '*'
for i in range(30):
    ret = ret + ' *'
print(ret)

Keďe na začiatku mal reťazec ret len jeden znak a potom sme k nemu na koniec pridali dva znaky ' *' a to sme spravili 30-krát, dostávame znakový reťazec dĺžky 61:

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Ešte zaujímavejčší je tento cyklus:

ret = '+'
for i in range(6):
    ret = ret + ret
print(ret)

Na začiatku ešte pre cyklom mal reťazec ret jeden znak '+'. V prvom prechode cyklu sme zreťazili dva reťazce '+' a '+' a výsledok sme priradili do ret. Teraz je v ňom '++'. V ďalšom prechode sa opäť zreťazí '++' a '++' a výsledok '++++' sa priradí do ret. Každý ďalší prechod cyklom zdvojnásobí počet znakov '+' v premennej ret. Po šiestom prechode cyklu bude v premennej ret znakový reťazec, ktorý bude obsahovať 64 znakov '+'. Po spustení programu naozaj dostaneme:

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Operácia viacnásobné zreťazenie

Videli sme, že pomocou for-cyklu a zreťazenia vieme vyrábať dosť dlhé znakové reťazce. Napr. aj zápis:

>>> vela = 'ahoj ' + 'ahoj ' + 'ahoj ' + 'ahoj ' + 'ahoj '
>>> vela
'ahoj ahoj ahoj ahoj ahoj '

Tento by sme vedeli zapísať for-cyklom, v ktorom by sa reťazec 'ahoj ' zlepil 5-krát za sebou tak, ako sa 30-krát zlepoval reťazec ' *'.

V Pythone je ďalšia operácia, tzv. viacnásobné zreťazenie, pomocou ktorej vieme veľmi elegantne zlepiť ten istý reťazec zadaný počet krát. Táto operácia sa zapisuje pomocou znamienka *: napr. číslo * reťazec alebo reťazec * číslo označuje, že sa daný reťazec zreťazí za seba určený počet krát. Napr.

>>> 'ahoj ' * 5
'ahoj ahoj ahoj ahoj ahoj '
>>> 20 * '*'
'********************'
>>> '>' * 10 + ' ' * 10 + '<' * 10
'>>>>>>>>>>          <<<<<<<<<<'
>>> 3 * ('xy' * 5 + ' O ')
'xyxyxyxyxy O xyxyxyxyxy O xyxyxyxyxy O '

Uvedomte si, že takto sa dá vyrobiť dosť dlhý reťazec, s ktorým môže mať váš počítač už problém.

Pomocou viacnásobného zreťazenia vieme vyriešiť aj takúto úlohu: podprogram trojuholnik s parametrom n nakreslí zo znakov '*' takýto trojuholník (napr. pre n=8):

       *
      ***
     *****
    *******
   *********
  ***********
 *************
***************

Samotný program je takto jednoduchý:

def trojuholnik(n):
    for i in range(n):
        print(' ' * (n-i-1) + '*' * (2*i+1))

Zisťovanie dĺžky reťazca

Už vieme vyrábať znakové reťazce najrôznejších dĺžok. Python má štandardnú funkciu len, pomocou ktorej vieme zistiť dĺžku znakového reťazca. Napr.

>>> a = 'Python'
>>> len(a)
6
>>> b = a * 10
>>> b
'PythonPythonPythonPythonPythonPythonPythonPythonPythonPython'
>>> len(b)
60

Ešte sme sa zatiaľ asi nestretli s tzv. prázdnym reťazcom, to je reťazec, ktorý neobsahuje žiadne znaky. Zapisujeme ho ''. Môžeme zistiť aj jeho dĺžku:

>>> c = ''    # prázdny reťazec
>>> len(c)
0
>>> d = c * 10
>>> len(d)
0

Pripomeňme si for-cyklus, v ktorom sme veľakrát zdvojnásobovali dĺžku reťazca:

>>> x = 'a'
>>> for i in range(10):
        x = x + x
>>> len(x)
1024

Vidíme, že takto získame reťazec dĺžky 1024 (čo je 2 umocnené na 10, teda 2**10). Vieme vyrobiť aj takto veľký reťazec:

>>> y = 'a' * 100000000
>>> len(y)
100000000

čo je 100 miliónov, teda tento reťazec zaberá v pamäti až 100 MB (megabajtov). Nepokúšajte sa ale takýto reťazec vypisovať napr. pomocou print - Python s tým bude mať veľké problémy.

Čísla a reťazce

Už poznáme hodnoty troch typov: celé čísla (int), desatinné čísla (float) a znakové reťazce (str)- Tiež vieme, že operácia plus + vie sčítať dve čísla (napr. 2+3=5) alebo zreťaziť dva reťazce (napr. 'a'+'b'='ab'). Ale, ako je to s operáciou +, keď budeme sčitovať reťazec a číslo? Vyskúšajme:

>>> 'a' + 7
...
TypeError: must be str, not int
>>> '7' + 7
...
TypeError: must be str, not int

Podobne to dopadne, ak by sme sčitovali číslo s reťazcom. Python pri každej operácii robí kontrolu typov oboch operandov a hlasi chybu, ak sa táto operácia vykonať nedá. Podľa prvého operandu (čo je znakový reťazec) Python pochopil, že sme plánovali robiť zreťazovanie dvoch reťazcov a preto nám aj oznámil túto chybu.

Pozrite si tento program:

prve = input('zadaj prve cislo: ')
druhe = input('zadaj druhe cislo: ')
print('sucet zadanych cisel', prve, 'a', druhe, 'je', prve + druhe)

Dostávame napr.

zadaj prve cislo: 12
zadaj druhe cislo: 34
sucet zadanych cisel 12 a 34 je 1234

My už vieme, že príkaz input nám vždy priradí znakový reťazec a preto aj „súčet“ dvoch znakových reťazcov v skutočnosti zrealizoval zreťazenie dvoch reťazcov. Na tomto mieste by sme potrebovali prerobiť znakový reťazec na celé číslo. V Pythone máme na toto špeciálnu funkciu int, ktorá sa zo znakového reťazca vyrobí celé číslo. Nie náhodou má rovnaké meno ako meno typu int pre celé čísla - tejto funkcii hovoríme prevod reťazca na číslo. Skôr ako to použijeme v predchádzajúcom programe so súčtom, vyskúšajme túto funkciu v interaktívnom režime:

>>> int('1234')
1234
>>> int('7') + 7
14
>>> int('7a')
...
ValueError: invalid literal for int() with base 10: '7a'

Teraz môžeme opraviť náš program so súčtom dvoch prečítaných čísel:

prve = int(input('zadaj prve cislo: '))
druhe = int(input('zadaj druhe cislo: '))
print('sucet zadanych cisel', prve, 'a', druhe, 'je', prve + druhe)

Dostávame napr.

zadaj prve cislo: 12
zadaj druhe cislo: 34
sucet zadanych cisel 12 a 34 je 46

Všimnite si zápis int(input('zadaj prve cislo: ')). Takto sa zvykne zapisovať príkaz na čítanie vstupu z klávesnice, ak predpokladáme, že týmto vstupom je celé číslo. Týmto zápisom sa teda prečítaný reťazec automaticky prevedie na celé číslo.

Niekedy môžeme riešiť opačnú úlohu: potrebujeme zreťaziť znakový reťazec a celé číslo do jedného reťazca. Napr.

>>> cislo = 2 ** 10
>>> cislo
1024
>>> retazec = 'x' + cislo
...
TypeError: must be str, not int

Tu sme predpokladali, že sa nám vyrobí znakový reťazec 'x1024'. Už vieme, že takto to nebude fungovať. Musíme najprv z celého čísla 1024 vyrobiť znakový reťazec '1024' a až potom tento reťazec spojíme s reťazcom 'x'. Opäť potrebujeme funkciu, ktorá prerobí celé číslo na znakový reťazec (teda opačnú k funkcii int). Takou funkciu je str a ak je jej parametrom celé číslo, hovoríme tomu prevod reťazca na celé číslo. Opäť sa meno tejto funkcie zhoduje s menom typu pre znakové reťazce str.

Teraz to už zapíšeme správne:

>>> retazec = 'x' + str(cislo)
>>> retazec
'x1024'

Mohli sme to zapísať aj takto priamo:

>>> retazec = 'x' + str(2 ** 10)

Napíšeme podprogram oznac_bod, v ktorom využijeme funkciu str na prevod čísla na reťazec. Tento podprogram s dvomi parametrami x a y na tejto pozícii nakreslí bodku a pod ňou napíše súradnice tohro bodu v tvare, napr. '(57,22)':

import tkinter
import random

platno = tkinter.Canvas(bg='white')
platno.pack()

def oznac_bod(x, y):
    platno.create_oval(x-2, y-2, x+2, y+2, fill='red')
    retazec = '(' + str(x) + ',' + str(y) + ')'
    platno.create_text(x, y+8, text=retazec)

oznac_bod(57, 22)

for i in range(10):
    oznac_bod(random.randint(10, 350), random.randint(10, 220))

Pre otestovanie tohto podprogramu sme okrem bodu (57, 22) zvolili ešte nejakých 10 náhodných bodov. Dostávame takýto obrázok:

_images/17_04.png

Úlohy

  1. Napíšte program, v ktorom sú na začiatku priradenia do troch premenných a, b a c (alebo sú prečítané pomocou input). Napr.

    a = 'mam'
    b = 'rad'
    c = 'Python'
    

    Program potom vypíše všetkých 6 rôznych usporiadaní (permutácií) týchto troch hodnôt. Napr.

    mam rad Python
    Python rad mam
    rad Python mam
    ...
    

    Program by mal správne fungovať pre ľubovoľné hodnoty týchto troch premenných.

  1. Napíšte program, ktorý najprv pomocou input prečíta nejaký reťazec a potom ho vypíše po znakoch tak, že každý ďalší znak je vypísaný o kúsok vpravo a kúsok dole od predchádzajúceho. Napr.

    zadaj retazec: informatika
    

    Dostávame:

  2. Napíšte podprogram do_stlpca s jedným parametrom cislo, ktorý zadané celé číslo vypíše do grafickej plochy tak, že cifry tohto čísla budú zoradené v jednom stĺpci pod sebou. Napr. volanie do_stlpca(3**11) vytvorí tento obrázok v grafickej ploche: