previous up next hi englisch index

7. Řetězce

  1. Složený datový typ
  2. Délka
  3. Traverzování a smyčka 'for'
  4. Úseky řetězce
  5. Porovnávání řetězců
  6. Řetězce jsou neměnitelné
  7. Operátor 'in'
  8. Funkce 'find'
  9. Smyčka a počítadlo
  10. Nepovinné parametry
  11. Modul 'string'
  12. Vyhodnocení znaků
  13. Formátování řetězce
  14. Glosář
  15. Cvičení

7.1 Složený datový typ

Až dosud jsme poznali pět typů dat: int, float, bool, NoneType a str . Řetězce (strings) jsou kvalitativně odlišné od ostatních čtyř typů, protože se skládají z menších částic - alfanumerických znaků.

Typy, které se skládají z menších částic, se nazývají složené datové typy. Se složeným datovým typem můžeme chtít zacházet jako s jednou věcí, nebo můžeme chtít mít přístup k jeho částem. Tato dvojí možnost je užitečná.

Hranaté závorky slouží jako operátor, který vybere jednotlivé znaky z řetězce:

>>> fruit = "banana"
>>> letter = fruit[1]
>>> print letter

Výraz fruit[1] vybere znak číslo 1 z řetězce fruit. Proměnná letter se vztahuje k výsledku. Zobrazíme-li letter, dostaneme překvapení:

a

První písmeno slova 'banana' není a - alespoň ne pro obyčejného smrtelníka. Programátoři však z jakýchsi perverzních důvodů vždy počítají od nuly. Nulté písmeno ("nula-té") z banana je b, 1. pímeno ("jedna-té") je a a 2. ("dvě-té") je n.

Chceme-li první písmeno řetězce, zadáme 0 nebo jakýkoliv výraz s hodnotou 0 do hranatých závorek:

>>> letter = fruit[0]
>>> print letter
b

Výrazu v hranatých závorkách se říká index. Index označuje člena uspořádané sestavy, v tomto případě sestavy znaků v řetězci. Může jím být jakýkoliv celočíselný výraz.

7.2 Délka

Funkce len vrací počet znaků v řetězci:

>>> fruit = "banana"
>>> len(fruit)
6

Ve snaze získat poslední písmeno řetězce bychom mohli být v pokušení zkusit něco jako:

length = len(fruit)
last = fruit[length]       # chyba!

Toto nepůjde. Způsobí to chybu při běhu programu IndexError: string index out or range (index řetězce mimo rozsah). Důvod je ten, že ve slově banana není "6-té" písmeno. Protože jsme začali počítat od nuly, je šest písmen očíslováno od 0 do 5. Abychom dostali poslední znak, musíme od length odečíst 1:

length = len(fruit)
last = fruit[length-1]

Alternativně můžeme použít záporné indexy, které počítají od konce řetězce. Výraz fruit[-1] poskytne poslední písmeno, fruit[-2] předposlední, a tak dále.

7.3 Traverzování a smyčka 'for'

Mnohé výpočty zahrnují probírání znaků v řetězci postupně jeden za druhým. Počínaje prvním, s každým něco provedou, končí posledním. Tento způsob pojednání se jmenuje traverzování . Možný zápis tohoto postupu je pomocí příkazu while:

index = 0
while index < len(fruit):
  letter = fruit[index]
  print letter
  index = index + 1

Tato smyčka prochází řetězcem a zobrazí každé písmeno na samostatném řádku. Podmínka smyčky je index < len(fruit), takže když je index roven délce řetězce, je podmínka nepravdivá a tělo smyčky se neprovede. Poslední procházený znak je s indexem len(fruit)-1, což je poslední znak řetězce.

Použití indexu k traverzování sady hodnot je tak běžné, že Python poskytuje alternativu - jednodušší smyčku for:

for char in fruit:
  print char

V každém cyklu smyčky se přiřadí znak řetězce proměnné char. Smyčka projde postupně všemi znaky.

Následující příklad ukazuje použití zřetězení (concatenation) a smyčku for pro tvorbu abecedních řad. Abecedním je míněno uspořádání prvků řady či seznamu podle abecedy. Například, v knize Roberta McCloskeyho: Uvolněte cestu pro káčata se kachňata jmenují: Jack, Kack, Lack, Mack, Nack, Ouack, Pack a Quack. Tato smyčka vydá jejich jména v pořadí:

prefixes = "JKLMNOPQ"
suffix = "ack"

for letter in prefixes:   print letter + suffix

Výstup programu je tento:

Jack
Kack
Lack
Mack
Nack
Oack
Pack
Qack

V pořádku to úplně není, protože Ouack a Quack nejsou zapsáni správně. Tuto chybu napravíme v rámci cvičení.

7.4 Úseky řetězce

Část řetězce se nazývá úsek (slice). Výběr úseku je podobný jako výběr znaku:

>>> s = "Peter, Paul, and Mary"
>>> print s[0:5]
Peter
>>> print s[7:11]
Paul
>>> print s[17:21]
Mary

Operátor [n:m] vrací část řetězce od n-tého znaku včetně po znak před m-tým. Toto chování se příčí intuitivnímu přístupu; bude mít větší smysl, když si představíme, že indicie ukazují před jednotlivé znaky, viz obrázek:

Vynecháme-li první index (před dvojtečkou), začíná úsek na počátku řetězce. Vynecháme-li druhý index, úsek jde až ke konci řetězce. Takže:

>>> fruit = "banana"
>>> fruit[:3]
'ban'
>>> fruit[3:]
'ana'

Co asi znamená s[:]?

7.5 Porovnávání řetězců

Porovnávací operátory pracují také s řetězci. Rovnost řetězců zjistíme:

if word == "banana":
  print  "Yes, we have bananas!"

Jiné porovnávací operátory jsou užitečné při uspořádávání slov podle abecedy:

if word < "banana":
  print "Your word," + word + ", comes before banana."
elif word > "banana":
  print "Your word," + word + ", comes after banana."
else:
  print "Yes, we have bananas!"

Musíme si však být vědomi toho, že Python zachází s malými a velkými písmeny jinak, než jsme zvyklí. Všechna velká písmena řadí před všechna malá. V důsledku toho:

Your word, Zebra, comes before banana.

Obvyklý způsob řešení tohoto problému je přeměna řetězce do standardního formátu (např. všechna písmena malá) před prováděním porovnávání. Svízelnější problém je přinutit program, aby si uvědomil, že zebry nejsou ovoce.

7.6 Řetězce jsou neměnitelné

Může být lákavé použít operátor [] na levé straně přiřazení se záměrem změnit znak v řetězci. Na příklad:

greeting = "Hello, world!"
greeting[0] ='J'        # chyba!
print greeting

Místo výstupu Jello, world!, tento kód způsobí chybu při běhu programu: TypeError: 'str' object doesn´t support item assignment.

Řetězce jsou neměnitelné, což znamená, že existující řetězec nelze změnit. Nejlepší věc, kterou lze udělat, je vytvořit nový řetězec, který je variací původního:

greeting = "Hello, world!"
new_greeting = 'J' + greeting[1:]
print new_greeting

Řešením je zřetězení nového prvního písmena s úsekem řetězce greeting. Tato operace nemá žádný vliv na původní řetězec.

7.7 Operátor 'in'

Operátorin přezkoumá, zda je zadaný řetězec součástí jiného:

>>> 'p' in 'apple'
True
>>> 'i' in 'apple'
False
>>> 'ap' in 'apple'
True
>>> 'pa' in 'apple'
False
Všimněmež si, že řetězec může byt součástí sebe sama:
>>> 'a' in 'a'
True
>>> 'apple' in 'apple'
True

Použitím operátoru in a zřetězení můžeme napsat funkci, která odstraní všechny samohlásky z řetězce:

def remove_vowels(s):
    samohl = "aeiouyAEIOUY" 
    s_bez_samohl = ""
    for letter in s:
        if letter not in samohl:
            &nb s_bez_samohl += letter
    return s_bez_samohl           

Ověřme si, že funkce dělá to co jsme chtěli

7.8 Funkce 'find'

Co dělá následující funkce?

def find(strng, ch):
    index = 0
    while index < len(strng):
        if strng[index] == ch:
            return index
        index += 1
    return -1

V jistém smyslu je funkce find opakem operátoru [ ]. Místo přijmutí indexu a vyjmutí odpovídajícího znaku z řetězce, find přijme znak a nalezne index místa, kde se znak nachází. Není-li znak nalezen, funkce vrací -1.

V této funkci se poprvé setkáváme s příkazem return uvnitř smyčky. Je-li str[index] == ch, je funkce ukončena předčasným přerušením smyčky.

Není-li znak v řetězci obsažen, potom program opustí smyčku normálně a vrátí -1.

Tento způsob výpočtu je někdy nazýván traverzování Heuréka, protože jakmile nalezneme co hledáme, můžeme zvolat „Heuréka” a skončit hledání.

7.9 Smyčka a počítadlo

Následující program počítá kolikrát se písmeno a vyskytne v řetězci a je dalším příkladem počítadla, uvedeného v kapitole 6:

fruit = "banana"
count = 0
for char in fruit:
    if char == 'a':
        count += 1
print count

7.10 Nepovinné parametry

Pro nalezení místa prvního výskytu znaku po zadaném počátku hledání můžeme přidat třetí parametr do funkce find:

def find2(strng, ch, start):
    index = start
    whileindex < len(strng):
        if strng[index] == ch:
            return index
        index += 1
    return -1
Volání find2("banana","a",2) nyní vrátí pozici prvního a za indexem 2.

Ještě výhodnější bude kombinace fcí find a find2 přidáním volitelného parametru:

def find(strng, ch, start=0):
    index = start
    while index < len(strng):
        if strng[index] == ch:
            return index
        index += 1
    return -1
Výsledek volání find("banana","a",2) pro tuto verzi fce find bude stejný jako find2, zatímco při volání find("banana","a") bude parametr start nastaven na počáteční hodnotu 0.

Přidáním dalšího volitelného parametru do fce find zajistíme prohledávání jak dopředu, tak dozadu:

def find(strng, ch, start=0, step=1):
    index = start
    while 0 <= index < len(strng):
        if strng[index] == ch:
            return index
         index += step
    return -1
Zadání hodnoty -1 pro step způsobí zmenšování stavu počítadla. Pro tuto změnu bylo nutné ošetřit jak horní tak i dolní mez proměnné index.

7.11 Modul 'string'

Modul string obsahuje užitečné funkce pro manipulaci s řetězci. Jako obvykle, modul musíme importovat dřív než jej použijeme:

>>> import  string

Obsah modulu zjistíme vestavěnou fcí dir se jménem modulu jako argument:

>>> dir(string)
čímž dostaneme seznam prvků:

['Template', '_TemplateMetaclass', '__builtins__', '__doc__', '__file__', '__name__', '_float', '_idmap', '_idmapL', '_int', '_long', '_multimap', '_re', 'ascii_letters', 'ascii_lowercase', 'ascii_uppercase', 'atof', 'atof_error', 'atoi', 'atoi_error', 'atol', 'atol_error', 'capitalize', 'capwords', 'center', 'count', 'digits', 'expandtabs', 'find', 'hexdigits', 'index', 'index_error', 'join', 'joinfields', 'letters', 'ljust', 'lower', 'lowercase', 'lstrip', 'maketrans', 'octdigits', 'printable', 'punctuation', 'replace', 'rfind', 'rindex', 'rjust', 'rsplit', 'rstrip', 'split', 'splitfields', 'strip', 'swapcase', 'translate', 'upper', 'uppercase', 'whitespace', 'zfill']

Typ prvku zjistíme příkazem type. Při zadávání argumentu použijeme tečkovou notaci

>>> type (string.digits)
<type 'str'>
>>> type (string.find)
<type 'function'>
Protože string.digits je řetězec, můžeme jej vytisknout a zjistit co obsahuje:
>>> print(string.digits)
0123456789
Že obsahuje všechny dekadické číslice nás nepřekvapí.

string.find je funkce, která dělá skoro to samé jako funkce, kterou jsme sami napsali. Bližší informaci získáme vytištěním jejího dokumentačního řetězce (docstring) __doc__:

>>> print string.find.__doc__
find(s,sub [,start [,end]]) -> in

    Return the lowest index in s where substring sub is found,
          such that sub is contained within s[start,end]. Optional
          arguments start and end are interpreted as in slice notation.

          Return -1 on failure.

Parametry v hranatých závorkách jsou nepovinné. Fci string.find můžeme použít stejně jako naši find:

>>> fruit = "banana"
>>> index = string.find(fruit, "a")
>>> print index
1

Tento příklad ukazuje jednu z výhod modulů - s jejich použitím se můžeme vyhnout kolizím mezi jmény vestavěných a uživatelsky definovaných funkcí. Pomocí tečkové notace můžeme určit kterou verzi find chceme.

Funkce string.find je ve skutečnosti všestrannější než naše verze. Umí nalézt i části řetězců, nejenom pouhé znaky:

>>> string.find("banana", "na")
2

Jako naše i tato přijímá dodatečný argument, který určuje index u kterého má začít:

>>> string.find("banana", "na",3)
4

Odlišně od naší, její druhý nepovinný parametr určuje index, u kterého má hledání skončit:

>>> string.find("bob", "b",1,2)
-1

V tomto případě skončí hledání nezdarem, protože se písmeno b nevyskytuje v intervalu od 1 do 2 (nikoliv včetně).

7.12 Vyhodnocení znaků

Často bývá užitečné otestovat jednotlivý znak a zjistit, zda je to velké či malé písmeno, nebo zda to je číslice. Modul string poskytuje několik konstant, které jsou pro tyto účely vhodné.

Funkce string.lowercase obsahuje všechna písmena, která systém řadí mezi malá. Podobně string.uppercase obsahuje všechna velká písmena. Vyzkoušejme si následující a uvidíme, co dostaneme:

>>> print string.lowercase
>>> print string.uppercase
>>> print string.digits

Tyto konstanty a funkci find můžeme použit k vyhodnocení znaků. Vrátí-li například string.lowercase,ch) hodnotu jinou než -1, pak ch musí být malé písmeno:

def is_lower(ch):
    return string.find(string.lowercase, ch) != -1

Případně můžeme využít operátoru in, který určí, zda se znak nachází v řetězci:

def is_lower(ch):
    return ch in string.lowercase

Jako další alternativu můžeme použít porovnávací operátor:

def is_lower(ch):
    return 'a' <= ch <= 'z'

Je-li ch mezi a a z, pak to musí být malé písmeno.

Další konstanta z modulu string nás možná překvapí, když ji vytiskneme:

>>> print string.whitespace

Znaky whitespace posouvají kurzor, aniž by se cokoli tisklo. Vytvářejí bílé místo (alespoň na bílém papíře) mezi viditelnými znaky. Konstanta string.whitespace obsahuje všechny znaky whitespace, včetně space, tab (\t) a newline (\n).

V modulu string je ještě celá řada dalších užitečných funkcí, ale tato kniha není míněna jako referenční příručka na rozdíl od Python Library Reference. Spolu s haldou další dokumentace je přístupná na www.python.org.

7.13 Formátování řetězců

Nejjednodušší a přitom účinný způsob formátování řetězce je s použitím operátoru pro formátování řetězce % spolu s formátovacími operacemi Pythonu. Nejlépe si to ukážeme na několika příkladech:

>>> "Jmenuje se %s." % "Arthur"
'Jmenuje se Arthur'
>>> name = "Alice"
>>> age = 10
>>> "I am %s" and I am %d years old." % (name, age)
'I am Alice and I am 10 years old'
>>> n1 = 4
>>> n2 = 5
>>> "2**10 = %d and %d*%d = %f"  %  (2**10, n1, n2, n1*n2)
'2**10 = 1024 and 4*5 = 20.000000'
>>> 

Schema formátování řetězce vypadá takto:

"<FORMAT >"  %  "<VALUES >"
Část format obsahuje pořadí znaků a konverzní specifikace. Konverzní specifikace se skládají z operátoru % a označení typu hodnoty. Následuje samotný znak % a poté pořadí hodnot, pro každou specifikaci jedna. Závorky jsou nepovinné, je-li hodnota pouze jedna.

V prvním uvedeném příkladě je jediná konverzní specifikace %s, která označuje řetězec. K ní se přiřazuje jediná hodnota "Arthur" a není uzavřena v závorkách.

Ve druhém příkladě má name hodnotu řetězce "Alice" a age má hodnotu celého čísla 10. Tyto se přiřazují ke dvěma konverzním specifikacím %s a %d; druhá specifikace je označením celého dekadického čísla.

Ve třetím příkladě mají proměnné n1 a n2 celočíselné hodnoty 4 a 5. Ve formátovaném řetězci jsou čtyři konverzní specifikace: tři %d a jedna %f. Písmeno f naznačuje, že příslušná hodnota má být ve tvaru čísla s plovoucí desetinnou čárkou. Čtyři hodnoty, které se vztahují k uvedeným čtyřem konverzním specifikacím jsou: 2**10,n1,n2 a n1*n2.

Znaky s,d a f jsou všechny konverzní typy, které v této knize budeme potřebovat. Jejich úplný seznam můžeme nalézt v části String Formatting Operations referenční příručky Python Library Reference.

Následujicí problém ilustruje praktické užití formátování řetězce:

i = 1
print "i\ti**2\ti**3\ti**5\ti**10\ti**20"
while i <= 10:
   print i, '\t', i**2, '\t', i**3, '\t', i**5, '\t', i**10, '\t', i**20
   i += 1    

Tento program vytiskne tabulku různých mocnin čísel od 1 do 10. V uvedeném tvaru je rovnání výsledků do sloupců způsobené znakem tabelátoru (\t) které selhává jakmile hodnoty výsledků zaberou 8 míst:

i       i**2    i**3    i**5    i**10   i**20
1       1       1       1       1       1
2       4       8       32      1024    1048576
3       9       27      243     59049   3486784401
4       16      64      1024    1048576         1099511627776
5       25      125     3125    9765625         95367431640625
6       36      216     7776    60466176        3656158440062976
7       49      343     16807   282475249       79792266297612001
8       64      512     32768   1073741824      1152921504606846976
9       81      729     59049   3486784401      12157665459056928801
10      100     1000    100000  10000000000     100000000000000000000

Mohli bychom změnit šířku sloupce ale vidíme, že první sloupce již teď mají více místa než potřebují. Nejlepší bude určit šířku pro každý sloupec jednotlivě. Jak lze tušit, řešení poskytuje formátovaný řetězec :

i = 1 
print "%-4s%-5s%-6s%-8s%-13s%-15s" %
   ('i', 'i**2', 'i**3', 'i**5', 'i**10', 'i**20')
while i <= 10:
   print "%-4d%-5d%-6d%-8d%-13d%-15d" % (i, i**2, i**3, i**5, i**10, i**20)
   i += 1    

Běh této verze dává následující výstup:

i   i**2 i**3  i**5    i**10        i**20          
1   1    1     1       1            1              
2   4    8     32      1024         1048576        
3   9    27    243     59049        3486784401     
4   16   64    1024    1048576      1099511627776  
5   25   125   3125    9765625      95367431640625 
6   36   216   7776    60466176     3656158440062976
7   49   343   16807   282475249    79792266297612001
8   64   512   32768   1073741824   1152921504606846976
9   81   729   59049   3486784401   12157665459056928801
10  100  1000  100000  10000000000  100000000000000000000

Pomlčka - za každou konverzní specifikací určuje zarovnání zleva. Číselné hodnoty určují minimální délku, takže %-13d je minimálně třináctimístné číslo zarovnané zleva.

7.14 Glosář

složený datový typ (compound data type)
Datový typ, v němž hodnoty jsou tvořeny z prvků, které jsou samy hodnotami.
sekvence (sequence)
Uspořádaná množina prvků - sled. Podmnožina této množiny se nazývá subsekvence.
index
Proměnná či hodnota, použitá k označení prvku uspořádané řady, například znaku v řetězci.
traverzovat (traverse)
Probírat postupně jednotlivé prvky dané řady a s každým provést obdobnou operaci.
úsek (slice)
Část řetězce, vymezená rozsahem indexů. Obecněji, subsekvence každé sekvence v Pythonu může být vytvořena použitím úsekového operátoru (sekvence[start:stop]).
neměnitelný datový typ (immutable data type)
Složený datový typ, jehož položky nemohou být měněny.
nepovinný parametr (optional parameter)
Parametr uvedený v záhlaví funkce s přiřazenou počáteční hodnotou, která se použije, když při volání funkce není pro tento parametr zadán argument.
tečková notace (dot natation)
Použití tečky (dot operator) při volání funkce uvnitř modulu.
dokumentační řetězec (docstring)
Řetězcová konstanta, uvedená na prvním řádku funkce nebo modulu (a jak uvidíme později i třídy a metody).
whitespace
Netištěné znaky pro ovládání kurzoru. Všechny tyto znaky obsahuje konstanta string.whitespace.

7.15 Cvičení

  1. Uprav sled příkazů tak, aby se Ouack a Quack vytiskly správně:
    prefixes = "JKLMNOPQ"
    suffix = "ack"
    
    for letter in prefixes:
      print letter + suffix
    
  2. Zapouzdři následující sekvenci příkazů do funkce count_letters
    fruit = "banana"
    count = 0
    for char in fruit:
      if char == 'a':
        count = count + 1
    print count
    
    a zobecni ji tak, aby brala řetězec a písmeno jako argument.
  3. Nyní tuto funkci přepiš tak, aby místo traverzování řetězcem opakovaně volala funkci find (verzi z odst. 8.10). s nepovinným třetím parametrem.
  4. Která verze fce is_lower je nejrychlejší? Mohou být pro upřednostňování jedné verze před druhou jiné důvody než rychlost?
  5. Vytvoř soubor se jménem stringtools.py a vlož do ní následující řádky:
    def reverse (s):
       """
       >>> reverse ('happy')
       'yppah'
       >>> reverse ('Python') 
       'nohtyP'
       >>> reverse ('')
       ''
       >>> reverse ('P')
       'P'
       """
    
    if __name__ == '__main__':
        import doctest  
        doctest.testmod() 
    
    Přidej tělo funkce tak, aby prošla zkouškou doctestů.
  6. Přidej mirror do souboru stringtools.py.
    def mirror (s):
       """
       >>> mirror ('good')
       'gooddoog'
       >>> mirror ('yes') 
       'yessey'
       >>> mirror ('Python')
       'PythonnohtyP'
       >>> mirror ('')
       ''
       >>> mirror ('a')
       'aa'
       """
    
    Napiš tělo funkce tak, aby pracovala tak, jak naznačují doctesty.
  7. Přidej remove_letter do souboru stringtools.py.
    def remove_letter (letter, strng):
       """
       >>> remove_letter ('a', 'apple')
       'pple'
       >>> remove_letter ('a', 'banana') 
       'bnn'
       >>> remove_letter ('z', 'banana')
       'banana'
       >>> remove_letter ('i', 'Mississippi')
       'Msssspp'
       """
    
    Napiš tělo funkce tak, aby pracovala tak, jak naznačují doctesty.
  8. Konečně, přidávej postupně těla funkcím
    def is_palindrome (s):
       """
       >>> is_palindrome('abba')
       True
       >>> is_palindrome('abab') 
       False
       >>> is_palindrome('tenet')
       True
       >>> is_palindrome('banana')
       False
       >>> is_palindrome('straw warts')
       True
       """
    
    def count (sub, s):
       """
       >>> count('is', 'Mississippi')
       2
       >>> count('an', 'banana') 
       2
       >>> count('ana', 'banana')
       2
       >>> count('nana', 'banana')
       1
       >>> count('nanan', 'banana')
       0
       """
    
    def remove (sub, s):
       """
       >>> remove('an', 'banana') 
       'bana'
       >>> remove('cyc', 'bicycle')
       'bile'
       >>> remove('iss', 'Mississippi')
       'Missippi'
       >>> remove('egg', 'bicycle')
       'bicycle'
       """
    
    def remove_all (sub, s):
       """
       >>> remove_all('an', 'banana') 
       'ba'
       >>> remove_all('cyc', 'bicycle')
       'bile'
       >>> remove_all('iss', 'Mississippi')
       'Mippi'
       >>> remove('eggs', 'bicycle')
       'bicycle'
       """
    
    až všechny doctesty budou vyhovovat.
  9. Vyzkoušej následující formátující řetězcové operace v IRP (Python shell) a zaznamenej výsledky:
    1. "%s%d%f" % (5,5,5)
    2. "%-.2f" % 3
    3. "%-10.2f%-10.2f" % (7,1.0/2)
    4. print "$%5.2f\n $%5.2f\n $%5.2f" % (3,4.5,11.2)
  10. Následující formátované řetězce jsou chybné. Oprav chyby:
    1. "%s%s%s%s" % ('this', 'that', 'something')
    2. "%s%s%s" % ('yes', 'no', 'up', 'down')
    3. "%d%f%f" % (3, 3, 'three' )

previous up next hi englisch index