Cvičení č. 18 rozšířené o poznámky ze cvičení a řešení některých příkladů (ZS 2024/25)¶
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
).
# otevře soubor pro čtení v textovém režimu
file1 = open("data/data.txt", "r")
# přečte soubor a obsah uloží do proměnné file_content
file_content = file1.read()
# obsah souboru vypíšeme
print(file_content)
# soubor následně uzavřeme
file1.close()
Nějaký textový soubor, který obsahuje více řádků.
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.
with open('data/data.txt') as file1:
# přečte soubor a obsah uloží do proměnné file_content
file_content = file1.read()
# obsah souboru vypíšeme
print(file_content)
Nějaký textový soubor, který obsahuje více řádků.
with open('data/data.txt') as file1:
print(file1.read())
Nějaký textový soubor, který obsahuje více řádků.
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:
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()
:
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:
with open('data/data.txt') as file1:
for line in file1:
print(line, end='')
Nějaký textový soubor, který obsahuje více řádků.
# objekt reprezentující soubor je iterátor:
with open('data/data.txt') as file1:
print(file1)
print(type(file1))
<_io.TextIOWrapper name='data/data.txt' mode='r' encoding='UTF-8'> <class '_io.TextIOWrapper'>
# ukázky, že nemohu načíst data vícekrát
with open('data/data.txt') as file1:
for line in file1:
print(line, end='')
print("\n\nZnova:")
for line in file1:
print(line, end='')
Nějaký textový soubor, který obsahuje více řádků. Znova:
with open('data/data.txt') as file1:
r = file1.readline()
#r = file1.read()
for line in file1:
print(line, end='')
print("\n\nPrní řádek:")
print(r)
který obsahuje více řádků. Prní řádek: Nějaký textový soubor,
Je též možné načíst jednotlivé řádky do seznamu řetězců pomocí metody readlines()
a následně s nimi pracovat.
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ě:
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í.
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(text)
# zapíšeme text pomocí print()
print(text, 124, file=file1, sep = '\t', end = '')
# uzavřeme
file1.close()
Stejně jako u čtení, je vhodné použít konstrukci with
.
# otevřeme soubor pro zápis
with open('data/new_file.txt', "w") as file1:
file1.write("Nějaký text.\n")
file1.write("vice textu:" +str(67)+".\n")
file1.write(f"vice textu: {67}.\n")
print("více textu:", 124, ".", file=file1)
Pokud potřebujeme pracovat s více soubory najednou, můžeme je otevřít v rámci jedné konstrukce with
, například:
# zkopírování souboru:
with open('data/data.txt') as file1:
with open('data/data_new.txt', "w") as file2:
file2.write(file1.read())
# 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ů).
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')
file1.write(f"{i}\t{i ** 0.5:.2f}\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)
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']
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.41 sqrt( 3 ) = 1.73 sqrt( 4 ) = 2.0 sqrt( 5 ) = 2.24 sqrt( 6 ) = 2.45 sqrt( 7 ) = 2.65 sqrt( 8 ) = 2.83 sqrt( 9 ) = 3.0 Soucet odmocnin je: 19.310000000000002
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
.
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
Č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()
.
# Zápis pěti bytů do souboru v binární podobě
data = [1, 2, 65, 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()
.
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)
65 b'\x01\x02AB ' [1, 2, 65, 66, 32]
Příklady¶
- Načtěte textový soubor data/text.txt a vypište ho na obrazovku tak, že všechna písmena budou velká.
# nápověda:
"abc".upper()
'ABC'
with open("data/text.txt") as file1:
obsah = file1.read()
print(obsah.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!“
with open("data/text.txt") as file1:
for line in file1:
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!“
with open("data/text.txt", "r") as file1:
lines = file1.readlines()
for line in lines:
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!“
- Upravte předchozí příklad tak, aby byl výsledek konverze uložen do nového souboru a nevypisoval se na obrazovku.
with open("data/text.txt") as file1, open("data/text_new.txt", "w") as file2:
obsah = file1.read()
print(obsah.upper(), file=file2)
with open("data/text.txt", "r") as file1, open("data/text_novy.txt", "w") as file2:
for line in file1:
file2.write(line.upper())
#print(line.upper(), end = "", file=file2)
- V souboru data/text.txt spočítejte počet řádků a počet slov.
with open("data/text.txt") as file1:
pocet_radek = 0
pocet_slov = 0
for line in file1:
pocet_radek += 1
pocet_slov += len(line.split())
print(pocet_radek, pocet_slov)
11 47
with open("data/text.txt") as file1:
obsah = file1.read()
pocet_radek = len(obsah.split('\n'))
pocet_slov = len(obsah.split())
print(pocet_radek, pocet_slov)
11 47
- 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átux,y
(dvě čísla oddělená čárkou).
import math
min = 0
max = 2 * math.pi
step = 0.05
with open("data/sinus.txt", "w") as file1:
x = min
while x <= max:
y = math.sin(x)
file1.write(f"{x:.2f},{y:.5f}\n")
x += step
- 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ů.
# nápověda:
with open("data/inventory.txt", "r") as file1:
file1.readline()
for line in file1:
items = line.split(",")
print(int(items[1]), float(items[2]))
5 85.0 10 120.0 65 25.0 3 150.0 2 2000.0 560 0.5 1 5600.0 120 3.0
with open("data/inventory.txt") as file1:
pocet = 0
cena = 0
next(file1)
for line in file1:
sline = line.split(",")
pocet += int(sline[1])
cena += float(sline[2])
print(pocet, cena)
766 7983.5
- 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>
- 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
...