Dnes se podíváme na to, jak v Pythonu číst z (a pak i zapisovat do) souborů.
Ke čtení textu ze souboru jsou potřeba tři kroky:
Vytvoř si v editoru soubor basnicka.txt
a napiš do něj libovolnou básničku.
Soubor ulož.
Na uložení souboru s básničkou doporučuji použít stejný editor, jaký používáš na Pythonní programy.
Používáš-li jiný editor než Atom, dej si při ukládání pozor na kódování:
'utf-8'
nestandardní 'utf-8-sig'
.Ono utf-8
je název standardního kódování.
Zajišťuje, že se případné emoji nebo znaky s diakritikou do souboru uloží
tak, aby se daly přečíst i na jiném počítači či operačním systému.
🎉
Potom napiš tento program:
soubor = open('basnicka.txt', encoding='utf-8')
obsah = soubor.read()
print(obsah)
soubor.close()
a spusť ho z adresáře, ve kterém je
basnicka.txt
(jinými slovy, aktuální adresář musí být ten, který
obsahuje soubor s básničkou).
Obsah souboru se vypíše!
Co se tu děje?
Tak jako int()
vrací čísla a input()
řetězce, funkce
open()
vrací hodnotu, která představuje otevřený soubor.
Tahle hodnota má vlastní metody.
Tady používáme metodu read()
, která
najednou přečte celý obsah souboru a vrátí ho jako řetězec.
Na metodu close()
, která otevřený soubor zavírá, se podíváme později.
Otevřené soubory se, jako např. řetězce či range
,
dají použít s příkazem for
.
Tak jako for i in range
poskytuje za sebou jdoucí čísla a for c in 'abcd'
poskytuje jednotlivé znaky řetězce, for radek in soubor
bude do proměnné
radek
dávat jednotlivé řádky čtené ze souboru.
Například můžeme básničku odsadit, aby se vyjímala v textu:
print('Slyšela jsem tuto básničku:')
print()
soubor = open('basnicka.txt', encoding='utf-8')
for radek in soubor:
print(' ' + radek)
soubor.close()
print()
print('Jak se ti líbí?')
Když to zkusíš, zjistíš, že trochu nesedí řádkování. Zkusíš vysvětlit, proč tomu tak je?
Je docela důležité soubor potom, co s ním
přestaneš pracovat, zavřít (pomocí metody close()
).
Operační systémy mají limity na počet
současně otevřených souborů, které se nezavíráním
dají snadno překročit.
Na Windows navíc nemůžeš soubor, který je stále
otevřený, otevřít znovu.
Soubory se dají přirovnat k ledničce: abychom něco mohly z ledničky vzít, nebo dát dovnitř, musíme ji předtím otevřít a potom zavřít. Bez zavření to sice na první pohled funguje taky, ale pravděpodobně potom brzo něco zplesniví.
Zapomenout zavřít soubor je docela jednoduché:
například pokud by v rámci zpracování souboru
nastala výjimka nebo kdybys vyskočila z funkce
pomocí return
, náš předchozí kód by close
nezavolal,
a soubor by zůstal otevřený.
K tomu, abychom soubor nezapomněly v podobných
příkazech zavřít, slouží příkaz
try/finally
, který jsme si ukázaly v souvislosti
s výjimkami.
Pro připomenutí, finally
se provede vždycky – i když blok try
skončí
normálně, i když v něm nastane výjimka, i když z něj
„vyskočíš” pomocí return
či break
.
def iniciala():
"""Vrátí první písmeno v daném souboru."""
soubor = open('basnicka.txt', encoding='utf-8')
try:
obsah = soubor.read()
return obsah[0]
finally:
soubor.close()
print(iniciala())
Blok finally
se takhle dá použít vždycky,
když je potřeba něco ukončit nebo zavřít – ať už
je to soubor, nebo třeba připojení k databázi.
Protože je try/finally
celkem dlouhé a nepohodlné, má Python i příjemnější
variantu, příkaz with
:
def iniciala():
"""Vrátí první písmeno v daném souboru."""
with open('basnicka.txt', encoding='utf-8') as soubor:
obsah = soubor.read()
return obsah[0]
print(iniciala())
Tenhle příkaz jsme už viděly u testování,
kde uvozoval blok, ve kterém má nastat výjimka –
potom, co blok skončí, se zkontroluje, jestli
nastala a jestli je toho správného typu.
V našem případě se po skončení bloku
zavře soubor, ať už výjimka nastala nebo ne.
Podobně jako s finally
se zavře vždycky
– ať už blok with
skončil normálně,
výjimkou, nebo, jako tady, „vyskočením” ven.
V naprosté většině případů je pro práci se soubory
nejlepší použít with
.
Pozor!
Pro Python není problém smazat obsah jakéhokoli souboru. Psaní do souborů si zkoušej v adresáři, ve kterém nemáš uložené důležité informace!
Soubory se v Pythonu dají i zapisovat.
Pro zápis se soubor otevře pomocí pojmenovaného
argumentu mode='w'
(z angl.
mode, mód a write, psát).
Zapisovat jednotlivé řetězce se pak dá metodou
write
.
Pokud soubor už existuje, otevřením s mode='w'
se veškerý jeho obsah smaže.
Po zavření tak v souboru bude jen to, co do něj ve svém programu zapíšeš.
with open('druha-basnicka.txt', mode='w', encoding='utf-8') as soubor:
soubor.write('Naše staré hodiny\n')
soubor.write('Bijí čtyři hodiny\n')
Proč to \n?
Metoda write
neodřádkovává automaticky.
Chceš-li do souboru zapsat více řádků, je potřeba každý z nich ukončit
„ručně“, speciálním znakem '\n'
který jsme si popsaly
v sekci o řetězcích.
Případně se dá použít funkce print
,
která kromě do terminálu umí vypisovat i do otevřeného souboru,
a to pomocí pojmenovaného argumentu file
.
Ostatní možnosti funkce print
– automatické odřádkování,
převádnění na řetězce, možnost vypsat víc
hodnot najednou apod. – samozřejmě zůstávají.
with open('druha-basnicka.txt', mode='w', encoding='utf-8') as soubor:
print('Naše staré hodiny', file=soubor)
print('Bijí', 2+2, 'hodiny', file=soubor)
{ "data": { "sessionMaterial": { "id": "session-material:2018/pyladies-brno-jaro-st:foss:1", "title": "Soubory", "html": "\n \n \n\n <h1>Soubory</h1>\n<p>Dnes se podíváme na to, jak v Pythonu číst z\n(a pak i zapisovat do) souborů.</p>\n<p>Ke čtení textu ze souboru jsou potřeba tři kroky:</p>\n<ul>\n<li>soubor <em>otevřít</em>,</li>\n<li>něco z něj <em>přečíst</em></li>\n<li>a pak jej zase <em>zavřít</em>.</li>\n</ul>\n<p>Vytvoř si v editoru soubor <code>basnicka.txt</code> a napiš do něj libovolnou básničku.\nSoubor ulož.</p>\n<div class=\"admonition note\"><p>Na uložení souboru s básničkou doporučuji použít\nstejný editor, jaký používáš na Pythonní programy.</p>\n<p>Používáš-li jiný editor než Atom, dej si při ukládání pozor na kódování:</p>\n<ul>\n<li>Nabízí-li ti editor při ukládání výběr kódování, vyber UTF-8.</li>\n<li>Je-li k dispozici kódování „UTF-8 bez BOM”, použij to.</li>\n<li>Pokud musíš použít Notepad, který výše uvedené možnosti nemá, pak v kódu\nníže použij místo <code>'utf-8'</code> nestandardní <code>'utf-8-sig'</code>.</li>\n</ul>\n<p>Ono <a href=\"https://en.wikipedia.org/wiki/UTF-8\"><code>utf-8</code></a> je název standardního kódování.\nZajišťuje, že se případné emoji nebo znaky s diakritikou do souboru uloží\ntak, aby se daly přečíst i na jiném počítači či operačním systému.\n🎉</p>\n</div><p>Potom napiš tento program:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"n\">soubor</span> <span class=\"o\">=</span> <span class=\"nb\">open</span><span class=\"p\">(</span><span class=\"s1\">'basnicka.txt'</span><span class=\"p\">,</span> <span class=\"n\">encoding</span><span class=\"o\">=</span><span class=\"s1\">'utf-8'</span><span class=\"p\">)</span>\n<span class=\"n\">obsah</span> <span class=\"o\">=</span> <span class=\"n\">soubor</span><span class=\"o\">.</span><span class=\"n\">read</span><span class=\"p\">()</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">obsah</span><span class=\"p\">)</span>\n<span class=\"n\">soubor</span><span class=\"o\">.</span><span class=\"n\">close</span><span class=\"p\">()</span>\n</pre></div><p>a spusť ho z adresáře, ve kterém je\n<code>basnicka.txt</code> (jinými slovy, aktuální adresář musí být ten, který\nobsahuje soubor s básničkou).</p>\n<p>Obsah souboru se vypíše!</p>\n<p>Co se tu děje?\nTak jako <code>int()</code> vrací čísla a <code>input()</code> řetězce, funkce\n<code>open()</code> vrací hodnotu, která představuje <em>otevřený soubor</em>.\nTahle hodnota má vlastní metody.\nTady používáme metodu <code>read()</code>, která\nnajednou přečte celý obsah souboru a vrátí ho jako řetězec.\nNa metodu <code>close()</code>, která otevřený soubor zavírá, se podíváme později.</p>\n<h2>Iterace nad soubory</h2>\n<p>Otevřené soubory se, jako např. řetězce či <code>range</code>,\ndají použít s příkazem <code>for</code>.\nTak jako <code>for i in range</code> poskytuje za sebou jdoucí čísla a <code>for c in 'abcd'</code>\nposkytuje jednotlivé znaky řetězce, <code>for radek in soubor</code> bude do proměnné\n<code>radek</code> dávat jednotlivé řádky čtené ze souboru.</p>\n<p>Například můžeme básničku odsadit,\naby se vyjímala v textu:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Slyšela jsem tuto básničku:'</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">()</span>\n<span class=\"n\">soubor</span> <span class=\"o\">=</span> <span class=\"nb\">open</span><span class=\"p\">(</span><span class=\"s1\">'basnicka.txt'</span><span class=\"p\">,</span> <span class=\"n\">encoding</span><span class=\"o\">=</span><span class=\"s1\">'utf-8'</span><span class=\"p\">)</span>\n<span class=\"k\">for</span> <span class=\"n\">radek</span> <span class=\"ow\">in</span> <span class=\"n\">soubor</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">' '</span> <span class=\"o\">+</span> <span class=\"n\">radek</span><span class=\"p\">)</span>\n<span class=\"n\">soubor</span><span class=\"o\">.</span><span class=\"n\">close</span><span class=\"p\">()</span>\n<span class=\"k\">print</span><span class=\"p\">()</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Jak se ti líbí?'</span><span class=\"p\">)</span>\n</pre></div><p>Když to zkusíš, zjistíš, že trochu nesedí\nřádkování. Zkusíš vysvětlit, proč tomu tak je?</p>\n<div class=\"solution\" id=\"solution-0\">\n <h3>Řešení</h3>\n <div class=\"solution-cover\">\n <a href=\"/2018/pyladies-brno-jaro-st/beginners/files/index/solutions/0/\"><span class=\"link-text\">Ukázat řešení</span></a>\n </div>\n <div class=\"solution-body\" aria-hidden=\"true\">\n <p>Každý řádek končí znakem nového řádku (<code>'\\n'</code>).\nPři procházení souboru Python tento znak nechává na konci řetězce\n<code>radek</code> ¹.\nFunkce <code>print</code> pak přidá další nový řádek, protože ta na konci\nvýpisu vždycky odřádkovává – pokud nedostane argument <code>end=''</code>.\nTo je jeden způsob jak řádkování „spravit“; další je použít na každý řádek\nmetodu <code>rstrip</code>, která odstraní mezery a nové řádky z konce řetězce.</p>\n<hr>\n<p>¹ Proč to dělá? Kdyby <code>'\\n'</code> na konci řádků nebylo,\nnedalo by se např. dobře rozlišit, jestli poslední řádek\nkončí na <code>'\\n'</code></p>\n </div>\n</div><h2>Zavírání souborů</h2>\n<p>Je docela důležité soubor potom, co s ním\npřestaneš pracovat, zavřít (pomocí metody <code>close()</code>).\nOperační systémy mají limity na počet\nsoučasně otevřených souborů, které se nezavíráním\ndají snadno překročit.\nNa Windows navíc nemůžeš soubor, který je stále\notevřený, otevřít znovu.</p>\n<p>Soubory se dají přirovnat k ledničce: abychom něco\nmohly z ledničky vzít, nebo dát dovnitř, musíme\nji předtím otevřít a potom zavřít.\nBez zavření to sice na první pohled funguje taky,\nale pravděpodobně potom brzo něco zplesniví.</p>\n<p>Zapomenout zavřít soubor je docela jednoduché:\nnapříklad pokud by v rámci zpracování souboru\nnastala výjimka nebo kdybys vyskočila z funkce\npomocí <code>return</code>, náš předchozí kód by <code>close</code> nezavolal,\na soubor by zůstal otevřený.</p>\n<p>K tomu, abychom soubor nezapomněly v podobných\npříkazech zavřít, slouží příkaz\n<code>try/finally</code>, který jsme si ukázaly v souvislosti\ns výjimkami.</p>\n<p>Pro připomenutí, <code>finally</code> se provede vždycky – i když blok <code>try</code> skončí\nnormálně, i když v něm nastane výjimka, i když z něj\n„vyskočíš” pomocí <code>return</code> či <code>break</code>.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">iniciala</span><span class=\"p\">():</span>\n <span class=\"sd\">"""Vrátí první písmeno v daném souboru."""</span>\n\n <span class=\"n\">soubor</span> <span class=\"o\">=</span> <span class=\"nb\">open</span><span class=\"p\">(</span><span class=\"s1\">'basnicka.txt'</span><span class=\"p\">,</span> <span class=\"n\">encoding</span><span class=\"o\">=</span><span class=\"s1\">'utf-8'</span><span class=\"p\">)</span>\n <span class=\"k\">try</span><span class=\"p\">:</span>\n <span class=\"n\">obsah</span> <span class=\"o\">=</span> <span class=\"n\">soubor</span><span class=\"o\">.</span><span class=\"n\">read</span><span class=\"p\">()</span>\n <span class=\"k\">return</span> <span class=\"n\">obsah</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n <span class=\"k\">finally</span><span class=\"p\">:</span>\n <span class=\"n\">soubor</span><span class=\"o\">.</span><span class=\"n\">close</span><span class=\"p\">()</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">iniciala</span><span class=\"p\">())</span>\n</pre></div><p>Blok <code>finally</code> se takhle dá použít vždycky,\nkdyž je potřeba něco ukončit nebo zavřít – ať už\nje to soubor, nebo třeba připojení k databázi.</p>\n<h2>Příkaz with</h2>\n<p>Protože je <code>try/finally</code> celkem dlouhé a nepohodlné, má Python i příjemnější\nvariantu, příkaz <code>with</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">def</span> <span class=\"nf\">iniciala</span><span class=\"p\">():</span>\n <span class=\"sd\">"""Vrátí první písmeno v daném souboru."""</span>\n\n <span class=\"k\">with</span> <span class=\"nb\">open</span><span class=\"p\">(</span><span class=\"s1\">'basnicka.txt'</span><span class=\"p\">,</span> <span class=\"n\">encoding</span><span class=\"o\">=</span><span class=\"s1\">'utf-8'</span><span class=\"p\">)</span> <span class=\"k\">as</span> <span class=\"n\">soubor</span><span class=\"p\">:</span>\n <span class=\"n\">obsah</span> <span class=\"o\">=</span> <span class=\"n\">soubor</span><span class=\"o\">.</span><span class=\"n\">read</span><span class=\"p\">()</span>\n <span class=\"k\">return</span> <span class=\"n\">obsah</span><span class=\"p\">[</span><span class=\"mi\">0</span><span class=\"p\">]</span>\n\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">iniciala</span><span class=\"p\">())</span>\n</pre></div><p>Tenhle příkaz jsme už viděly u testování,\nkde uvozoval blok, ve kterém má nastat výjimka –\npotom, co blok skončí, se zkontroluje, jestli\nnastala a jestli je toho správného typu.\nV našem případě se po skončení bloku\nzavře soubor, ať už výjimka nastala nebo ne.\nPodobně jako s <code>finally</code> se zavře vždycky\n– ať už blok <code>with</code> skončil normálně,\nvýjimkou, nebo, jako tady, „vyskočením” ven.</p>\n<p>V naprosté většině případů je pro práci se soubory\nnejlepší použít <code>with</code>.</p>\n<h2>Psaní souborů</h2>\n<div class=\"admonition warning\"><p class=\"admonition-title\">Pozor!</p>\n<p>Pro Python není problém smazat obsah jakéhokoli souboru.\nPsaní do souborů si zkoušej v adresáři, ve kterém nemáš uložené\ndůležité informace!</p>\n</div><p>Soubory se v Pythonu dají i zapisovat.\nPro zápis se soubor otevře pomocí pojmenovaného\nargumentu <code>mode='w'</code> (z angl.\n<em>mode</em>, mód a <em>write</em>, psát).\nZapisovat jednotlivé řetězce se pak dá metodou\n<code>write</code>.</p>\n<p>Pokud soubor už existuje, otevřením s <code>mode='w'</code> se veškerý jeho obsah smaže.\nPo zavření tak v souboru bude jen to, co do něj ve svém programu zapíšeš.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">with</span> <span class=\"nb\">open</span><span class=\"p\">(</span><span class=\"s1\">'druha-basnicka.txt'</span><span class=\"p\">,</span> <span class=\"n\">mode</span><span class=\"o\">=</span><span class=\"s1\">'w'</span><span class=\"p\">,</span> <span class=\"n\">encoding</span><span class=\"o\">=</span><span class=\"s1\">'utf-8'</span><span class=\"p\">)</span> <span class=\"k\">as</span> <span class=\"n\">soubor</span><span class=\"p\">:</span>\n <span class=\"n\">soubor</span><span class=\"o\">.</span><span class=\"n\">write</span><span class=\"p\">(</span><span class=\"s1\">'Naše staré hodiny</span><span class=\"se\">\\n</span><span class=\"s1\">'</span><span class=\"p\">)</span>\n <span class=\"n\">soubor</span><span class=\"o\">.</span><span class=\"n\">write</span><span class=\"p\">(</span><span class=\"s1\">'Bijí čtyři hodiny</span><span class=\"se\">\\n</span><span class=\"s1\">'</span><span class=\"p\">)</span>\n</pre></div><div class=\"admonition note\"><p class=\"admonition-title\">Proč to \\n?</p>\n<p>Metoda <code>write</code> neodřádkovává automaticky.\nChceš-li do souboru zapsat více řádků, je potřeba každý z nich ukončit\n„ručně“, speciálním znakem <code>'\\n'</code> který jsme si popsaly\nv <a href=\"/2018/pyladies-brno-jaro-st/beginners/str/\">sekci o řetězcích</a>.</p>\n</div><p>Případně se dá použít funkce <code>print</code>,\nkterá kromě do terminálu umí vypisovat i do otevřeného souboru,\na to pomocí pojmenovaného argumentu <code>file</code>.\nOstatní možnosti funkce <code>print</code> – automatické odřádkování,\npřevádnění na řetězce, možnost vypsat víc\nhodnot najednou apod. – samozřejmě zůstávají.</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">with</span> <span class=\"nb\">open</span><span class=\"p\">(</span><span class=\"s1\">'druha-basnicka.txt'</span><span class=\"p\">,</span> <span class=\"n\">mode</span><span class=\"o\">=</span><span class=\"s1\">'w'</span><span class=\"p\">,</span> <span class=\"n\">encoding</span><span class=\"o\">=</span><span class=\"s1\">'utf-8'</span><span class=\"p\">)</span> <span class=\"k\">as</span> <span class=\"n\">soubor</span><span class=\"p\">:</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Naše staré hodiny'</span><span class=\"p\">,</span> <span class=\"nb\">file</span><span class=\"o\">=</span><span class=\"n\">soubor</span><span class=\"p\">)</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">'Bijí'</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"o\">+</span><span class=\"mi\">2</span><span class=\"p\">,</span> <span class=\"s1\">'hodiny'</span><span class=\"p\">,</span> <span class=\"nb\">file</span><span class=\"o\">=</span><span class=\"n\">soubor</span><span class=\"p\">)</span>\n</pre></div>\n\n\n " } } }