V minulém díle jste viděli...¶

Mezi nejužitečnější vlastnosti integrovaných vývojových prostředí je integrace nástrojů pro ladění kódu. Ladící program (slangově debugger) umožňuje za běhu sledovat hodnoty proměnných, vypisovat grafy volání funkcí, spouštět program po jednotlivých příkazech nebo spustit samostatně jen vybranou část kódu.

Ladění se sustí klávesovou zkratkou F5, po spuštění se naplní levý postraní panel Run and debug (Ctrl+Shift+D). V tomto panelu je například zobrazen seznam proměnných s aktuálními hodnotami, seznam bodů zastavení (breakpoints) nebo graf volání aktuální funkce.

Dalši užitečné nástroje pro krokování najdete v nabídce Run:

Položka Klávesová zkratka Krátký popis
Continue F5 obnoví vykonávání skriptu (až po další bod zastavení)
Step Over F10 vykoná následující příkaz
Step Into F11 vstoupí do těla volané funkce
Step Out Shift+F11 provede aktuální funkci a vrátí se do funkce volající
Toggle breakpoint F9 přepne bod zastavení na řádku s kurzorem

Práce se soubory 1¶

V programovacím jazyce Python lze přistupovat k souborům, které máme uložené na disku. Soubory lze číst, zapisovat do nich, vytvářet nové, mazat je a další operace.

Základní práce je docela přímočará. Pro práci se souborem používáme funkci open(), která dokáže soubor otevřít v několika různých módech, mezi které patří například čtení nebo zápis. Funkce open se většinou používá s jedním nebo dvěma parametry, kdy první určuje jméno souboru (včetně relativní nebo úplné cesty), se kterým chceme pracovat, a druhý určuje mód práce se souborem (čtení/zápis/...)

Funkce open() vrací objekt, který nám dovoluje následně se souborem pracovat.

Různé módy přístupu k souboru v Pythonu

Mód Popis
r Otevře soubor pro čtení. (výchozí)
w Otevře soubor pro zápis. Pokud soubor existuje, přemaže ho novým prázdným souborem.
x Vytvoří a otevře soubor, pokud na disku neexistuje. Pokud soubor již existuje, skončí to chybou.
a Otevře soubor pro zápis, aniž by smazal jeho obsah. Zápis bude probíhat za poslední data v souboru. Pokud soubor neexistuje, pak je vytvořen.
t Otevře soubor v textovém režimu. (výchozí)
b Otevře soubor v binárním režimu.
+ Otevře soubor pro upravení. (čtení a psaní)

Ukázky použití funkce open()

file1 = open("test.txt")        # otevře soubor pro čtení v textovém režimu
file2 = open("test.txt", "r")   # otevře soubor pro čtení v textovém režimu
file3 = open("test.txt", "w")   # otevře soubor pro zápis v textovém režimu
file4 = open("test.txt", "wb")  # otevře soubor pro zápis v binárním režimu
file5 = open("test.txt", "rb")  # otevře soubor pro čtení v binárním režimu
file6 = open("test.txt", "a")   # otevře soubor pro přidávání v textovém režimu
file7 = open("data/test3.txt")  # jméno souboru včetně relativní cesty
file8 = open("c:/data/test3.txt")  # jméno souboru včetně úplné cesty

Po práci se souborem je vhodné soubor uzavřít, čímž dojde k dokončení všech operací a zneplatnění ukazatele na soubor. K tomu slouží metoda close().

file1.close()  #uzavření souboru

Čtení textových souborů¶

Ze souboru otevřeného pro čtení můžeme číst data a ukládat je do proměnných v našem programu. Pro čtení lze využít například metodu read(), která data načte a vrátí je jako svou návratovou hodnotu (typu str).

In [1]:
# otevře soubor pro čtení v textovém režimu
file1 = open("data/data.txt", "r", encoding='UTF-8')
print(file1)
# přečte soubor a obsah uloží do proměnné file_content
file_content = file1.read()
file_content1 = file1.read()

# obsah souboru vypíšeme
print(file_content)

# soubor následně uzavřeme
file1.close()
<_io.TextIOWrapper name='data/data.txt' mode='r' encoding='UTF-8'>
Nějaký textový soubor,
který obsahuje
více řádků.
In [2]:
# ukázka, že nemohu načíst data vícekrát

# otevře soubor pro čtení v textovém režimu
file1 = open("data/data.txt", "r", encoding='UTF-8')
print(file1)
# přečte soubor a obsah uloží do proměnné file_content
file_content = file1.read()
file_content1 = file1.read()

# obsah souboru vypíšeme
print(file_content1)

# soubor následně uzavřeme
file1.close()

# ukázka, že nemohu načíst data vícekrát
<_io.TextIOWrapper name='data/data.txt' mode='r' encoding='UTF-8'>

Jiný způsob práce se souborem je využití klíčového slova with. Zde je výhoda v tom, že nemusíme volat close() a řešit uvolnění prostředků po nějaké chybě (například pokud do souboru nelze zapisovat nebo z něj číst). Také proměnná file1 má omezenou existenci pouze na tento blok, což zefektivňuje a zpřehledňuje kód.

In [4]:
with open('data/data.txt') as file1:
    #print(file1.read())
    file_content = file1.read()
    file_content1 = file1.read()

    # obsah souboru vypíšeme 
    print(file_content)

# širší ukázka
Nějaký textový soubor,
který obsahuje
více řádků.
In [6]:
# soubor neexistuje:
with open('data/data2.txt') as file1:
        print(file1.read())
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[6], line 1
----> 1 with open('data/data2.txt') as file1:
      2         print(file1.read())

File /usr/lib/python3.11/site-packages/IPython/core/interactiveshell.py:308, in _modified_open(file, *args, **kwargs)
    301 if file in {0, 1, 2}:
    302     raise ValueError(
    303         f"IPython won't let you open fd={file} by default "
    304         "as it is likely to crash IPython. If you know what you are doing, "
    305         "you can use builtins' open."
    306     )
--> 308 return io_open(file, *args, **kwargs)

FileNotFoundError: [Errno 2] No such file or directory: 'data/data2.txt'

Při práci se soubory může velmi lehce dojít k chybě při otevírání nebo další práci se souborem, například že soubor neexistuje, nelze z něj číst, atp. V tomto případě by náš program skončil chybou. Tyto chyby lze ale v Pythonu ošetřit pomocí výjimek (exceptions). Lze toho docílit tak, že funkce a metody pracující se soubory dáme do bloku try a následně výjimky odchytáváme. Více v příkladu:

In [8]:
try:
    with open('data/data2.txt') as file1:
        print(file1.read())
except OSError as e:
    print(f"Při práci se souborem došlo k chybě.\n{e}")

print("\nProgram ale pokračuje dál bez přerušení.")
Při práci se souborem došlo k chybě.
[Errno 2] No such file or directory: 'data/data2.txt'

Program ale pokračuje dál bez přerušení.

Poznámka: Funkce open vyvolává pouze výjimky typu OSError (operating system error). K jiným chybám ale může dojít ve vašem kódu uvnitř bloku with.

V následujících příkladech nebudeme tento způsob používat z důvodu zpřehlednění kódu, ale ve vašich programech by bylo vhodné tento způsob zpracování výjimek používat u každé práce se souborem.

Někdy je vhodné číst soubor po jednotlivých řádcích. Zde lze použít metodu readline():

In [4]:
with open('data/data.txt') as file1:
    line = file1.readline()
    while line:
        print(line, end='')
        line = file1.readline()
Nějaký textový soubor,
který obsahuje
více řádků.

Alternativně lze použít for-cyklus, kde jako zdroj hodnot použijeme soubor:

In [10]:
with open('data/data.txt') as file1:
    file1.readline()
    for line in file1:
        print(line, end='')

# opět ukázka, že nemohu načíst data víckrát
který obsahuje
více řádků.

Je též možné načíst jednotlivé řádky do seznamu řetězců pomocí metody readlines() a následně s nimi pracovat.

In [6]:
with open('data/data.txt') as file1:
    lines = file1.readlines()
print("Vsechna data v seznamu:\n", lines, "\n")
print("Vypsani 2. radku:\n",lines[1])
Vsechna data v seznamu:
 ['Nějaký textový soubor,\n', 'který obsahuje\n', 'více řádků.'] 

Vypsani 2. radku:
 který obsahuje

Alternativně:

In [11]:
with open('data/data.txt') as file1:
    lines = list(file1)
print("Vsechna data v seznamu:\n", lines, "\n")
print("Vypsani 2. radku:\n",lines[1])
Vsechna data v seznamu:
 ['Nějaký textový soubor,\n', 'který obsahuje\n', 'více řádků.'] 

Vypsani 2. radku:
 který obsahuje

Zápis do textových souborů¶

Do souborů je samozřejmě možné zapisovat. K tomu slouží metoda write() nebo funkce print(). Metoda write() má jediný parametr typu str. V případě funkce print(), kterou už dobře známe, je třeba specifikovat (pomocí parametru file), do kterého souboru se data mají zapsat. Když parametr nezadáme, použije se standardní výstup, jak jsme byli doposud zvyklí.

In [17]:
text = "Nějaký text, který chceme zapsat do souboru."

# otevřeme soubor pro zápis v textovém režimu
# pokud neexistuje, je nově vytvořen
file1 = open("data/new_file.txt", 'w')

# zapíšeme text pomocí write()
file1.write("vice textu"+str(67)+"\n")
file1.write("vice textu"+str(67)+"\n")
file1.write(f"vice textu {67}. \n")

# zapíšeme text pomocí print()
print(text,124, file=file1) 

# uzavřeme
file1.close()

# ukázka ... write víckrát, čísla (str, +, fstring).

Stejně jako u čtení, je vhodné použít konstrukci with.

In [9]:
# otevřeme soubor pro zápis
with open('data/new_file.txt', "w") as file1:
    file1.write("Nějaký text.\n")
    print("Nějaký další text pomocí print().", file=file1)

Pokud potřebujeme pracovat s více soubory najednou, můžeme je otevřít v rámci jedné konstrukce with, například:

In [19]:
# zkopírování souboru:
with open('data/data.txt') as file1:
    with open('data/data_new.txt', "w") as file2:
        file2.write(file1.read())
In [21]:
# zkopírování souboru:
with open('data/data.txt') as file1, open('data/data_new.txt', "w") as file2:
        file2.write(file1.read())
Ukázkový příklad 1: zápis číselných hodnot do souboru¶

Zapíšeme 2 sloupce čísel oddělené tabulátorem (znak '\t'), kde v prvním sloupci bude vždy číslo (od 1 do n) a v druhém jeho odmocnina. Při zápisu číselných hodnot do souboru pomocí metody write() je nejprve musíme převést na stringy. To můžeme udělat například pomocí funkce str() nebo pomocí formátovacích řetězců (f-stringů).

In [22]:
import math
n = 10
with open('data/cisla.txt', "w") as file1:
    for i in range(1, n):
        # s využitím funkce str() a metody write()
        file1.write(str(i) + '\t'+ str(math.sqrt(i))+'\n')
        
        # alternativně pomocí f-stringu:
        # file1.write(f"{i}\t{math.sqrt(i)}\n")

        # alternativně pomocí f-stringu a funkce print():
        # print(f"{i}\t{math.sqrt(i)}", file=file1)
In [7]:
soucet = 0
with open('data/cisla.txt', "r") as file1:
    for line in file1:
        s = line.split()
        print("sqrt (",s[0],") = ",s[1])
        #print(f"sqrt ( {s[0]} ) = {s[1]}")
        soucet += float(s[1])
print(soucet)
sqrt ( 1 ) =  1.0
sqrt ( 2 ) =  1.4142135623730951
sqrt ( 3 ) =  1.7320508075688772
sqrt ( 4 ) =  2.0
sqrt ( 5 ) =  2.23606797749979
sqrt ( 6 ) =  2.449489742783178
sqrt ( 7 ) =  2.6457513110645907
sqrt ( 8 ) =  2.8284271247461903
sqrt ( 9 ) =  3.0
19.30600052603572
Ukázkový příklad 2: načtení číselných hodnot ze souboru, jejich výpis a spočítání součtu odmocnin¶

Při čtení dat z textového souboru je musíme vhodně zpracovat (parsovat), abychom s nimi pak mohli pracovat. Hodnota načtená pomocí metod read(), readline() nebo pomocí for-cyklu je typu str (string). String musíme nejprve rozdělit na jednotlivé podstringy a ty následně převést na vhodný datový typ.

Rozdělení stringu lze provést pomocí nám již známé metody split(), která daný string rozdělí na seznam kratších stringů v místech, kde se nacházel oddělovač. Například: "rum,pivo,zelena".split(',') == ['rum', 'pivo', 'zelena']

In [12]:
with open('data/cisla.txt') as file2:
    sum = 0  # budeme počítat sumu z druhého sloupce
    for line in file2:
        string_values = line.split('\t')  # rozdělíme řádek podle znaku \t
        index = int(string_values[0])  # první hodnotu převedeme na int
        value = float(string_values[1])  # druhou převedeme na float
        
        sum += value  # přičteme k sumě
        print('sqrt(', index, ') = ', value)  # ukázkový výpis

print('Soucet odmocnin je:', sum)
sqrt( 1 ) =  1.0
sqrt( 2 ) =  1.4142135623730951
sqrt( 3 ) =  1.7320508075688772
sqrt( 4 ) =  2.0
sqrt( 5 ) =  2.23606797749979
sqrt( 6 ) =  2.449489742783178
sqrt( 7 ) =  2.6457513110645907
sqrt( 8 ) =  2.8284271247461903
sqrt( 9 ) =  3.0
Soucet odmocnin je: 19.30600052603572

Nastavení standardního výstupu¶

Pokud nechceme stále zadávat parametr file při použití funkce print(), lze nastavit standardní výstup do námi zvoleného souboru. Lze to provést pomocí nastavení sys.stdout na náš soubor. Je nutné importovat balíček sys.

In [27]:
import sys  # importujeme balíček sys

# uložíme si původní hodnotu výstupu
orig_stdout = sys.stdout

with open('data/soubor.txt', 'w') as file1:
    # změníme výstup na náš soubor
    sys.stdout = file1
    print('Tento text se zapíše do souboru.')

# změníme výstup zpět na standardní výstup
sys.stdout = orig_stdout
print("neco")
neco

Čtení a zápis binárních souborů¶

Práce s binárními soubory je velmi podobná. Používáme stejné funkce a metody open(), read(), write() a close(). Rozdíl je hlavně v tom, že binární soubory jsou pro člověka nečitelné a data jsou v něm uložena právě v binární, nikoli textové podobě.

S binárními soubory je spojeno několik komplikací. Nejprve musíme naše data převést na byty a pak zapsat. Navíc je třeba dát pozor na kódování čísel větších než 1 byte. Zde podle architektury procesoru existuje kódování little endian a big endian, které se liší pořadím bytu.

Pro převedení seznamu s hodnotami na pole bytů lze například použít funkci bytearray().

In [30]:
# Zápis pěti bytů do souboru v binární podobě
data = [1, 2, 255, 66, 32]
bytes = bytearray(data)
with open('data/binarni_soubor.bin', "wb") as file2:
    file2.write(bytes)

Čtení binárních souborů se provádí pomocí metody read().

In [31]:
with open('data/binarni_soubor.bin', "rb") as file2:
    content = file2.read() # přečteme vše
    print(content[2])      # přistoupíme ke třetímu bytu ze souboru
    print(content)
    data = list(content)   # převod bytového pole na seznam čísel
    print(data)
255
b'\x01\x02\xffB '
[1, 2, 255, 66, 32]

Příklady¶

  1. Načtěte textový soubor data/text.txt a vypište ho na obrazovku tak, že všechna písmena budou velká.
In [17]:
with open('data/text.txt') as file_in:
    print(file_in.read().upper())
OKOLO LESA POLE LÁN,
HOJ JEDE, JEDE Z LESA PÁN,
NA VRANÉM BUJNÉM JEDE KONI,
VESELE PODKOVIČKY ZVONÍ,
JEDE SÁM A SÁM.

A PŘED CHALUPOU S KONĚ HOP
A NA CHALUPU: KLOP, KLOP, KLOP!
„HOLA HEJ! OTEVŘTE MI DVÉŘE,
ZBLOUDIL JSEM PŘI LOVENÍ ZVĚŘE,
DEJTE VODY PÍT!“
In [19]:
with open('data/text.txt') as file_in:
    for line in file_in:
        print(line.upper(), end="")
OKOLO LESA POLE LÁN,
HOJ JEDE, JEDE Z LESA PÁN,
NA VRANÉM BUJNÉM JEDE KONI,
VESELE PODKOVIČKY ZVONÍ,
JEDE SÁM A SÁM.

A PŘED CHALUPOU S KONĚ HOP
A NA CHALUPU: KLOP, KLOP, KLOP!
„HOLA HEJ! OTEVŘTE MI DVÉŘE,
ZBLOUDIL JSEM PŘI LOVENÍ ZVĚŘE,
DEJTE VODY PÍT!“
  1. Upravte předchozí příklad tak, aby byl výsledek konverze uložen do nového souboru a nevypisoval se na obrazovku.
In [21]:
with open('data/text.txt') as source:
    with open('data/text_novy.txt','w') as dest:
        dest.write(source.read().upper())
In [23]:
with open('data/text.txt') as source, open('data/text_novy1.txt','w') as dest:
    for line in source:
        dest.write(line.upper())
        #print(line.upper(), file=dest, end="")
  1. V souboru data/text.txt spočítejte počet řádků a počet slov.
In [8]:
pocet_radek = 0
pocet_slov = 0
with open('data/text.txt') as src:
    for line in src:
        pocet_radek += 1
        pocet_slov += len(line.split())

print('Pocet radek:', pocet_radek, ' Pocet slov:', pocet_slov)
Pocet radek: 11  Pocet slov: 47
  1. Vytvořte program, který do textového souboru data/sinus.txt vypíše dvojice hodnot $x,y$ kde $x$ budou čísla od $0$ do $2\pi$ s krokem $0.05$ a $y = \sin(x)$. Každá dvojice bude vypsaná na samostatný řádek ve formátu x,y (dvě čísla oddělená čárkou).
In [10]:
import math
min = 0
max = 2.0 * math.pi
step = 0.05  

# Otevření souboru pro zápis
with open('data/sin_values.txt', 'w') as file:
    x = min
    while x <= max:
        file.write(f"{x},{math.sin(x)}\n")
        x += step
  1. Načtěte soubor data/inventory.txt, ve kterém je seznam zboží na skladě. Z dat vypočtěte celkovou cenu zboží na skladě a také celkový počet kusů všeho zboží dohromady. Výsledek vypište (na standardní výstup). Podívejte se nejprve na strukturu souboru a podle toho ho v programu zpracujte. Všimněte si například, že první řádek obsahuje jen popis sloupců.
In [ ]:
 
  1. Data v souboru data/inventory.txt převeďte z formátu CSV (hodnoty oddělené čárkou) na tabulku v HTML (do nějakého jiného souboru). Příklad:
<table>
    <tr>
        <td>Kýbl</td>
        <td>5</td>
        <td>85</td>
    </tr>
    <tr>
        <td>Pytel cementu</td>
        <td>10</td>
        <td>12</td>
    </tr>
</table>
In [ ]:
 
  1. Vypište do souboru čísla od 2 do n a vedle nich do sloupce odděleného tabulátorem (znak '\t') jejich prvočíselný rozklad oddělený čárkami. Příklad:
...
8    2,2,2
9    3,3
10   2,5
...
In [ ]: