Soubory

Dnes se podíváme na to, jak v Pythonu číst z (a pak i zapisovat do) souborů.

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í:

  • Nabízí-li ti editor při ukládání výběr kódování, vyber UTF-8.
  • Je-li k dispozici kódování „UTF-8 bez BOM”, použij to.
  • Pokud musíš použít Notepad, který výše uvedené možnosti nemá, pak v kódu níže použij místo '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()
soubor.close()

print(obsah)

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. Nakonec metoda close() otevřený soubor zase zavře.

Automatické zavírání souborů

Soubory se dají přirovnat k ledničce: abys něco mohla z ledničky vzít, nebo dát dovnitř, musíš 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í.

Stejně tak je docela důležité soubor zavřít po tom, co s ním přestaneš pracovat. Bez zavření to na první pohled funguje, ale složitější programy se můžou dostat do problémů. 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.

Na korektní zavření souboru ale programátoři často zapomenou. Proto Python poskytuje příkaz with, který soubory zavírá automaticky. Používá se takhle:

with open('basnicka.txt', encoding='utf-8') as soubor:
    obsah = soubor.read()

print(obsah)

Příkaz with vezme otevřený soubor (který vrací funkce open) a přiřadí ho do proměnné soubor. Pak následuje odsazený blok kódu, kde se souborem můžeš pracovat – v tomhle případě pomocí metody read přečíst obsah jako řetězec. Když se Python dostane na konec odsazeného bloku, soubor automaticky zavře.

V naprosté většině případů je pro otevírání souborů nejlepší použít with.

Iterace nad soubory

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ůžeš básničku odsadit, aby se vyjímala v textu:

print('Slyšela jsem tuto básničku:')
print()

with open('basnicka.txt', encoding='utf-8') as soubor:
    for radek in soubor:
        print('    ' + radek)

print()
print('Jak se ti líbí?')

Když to zkusíš, zjistíš, že trochu nesedí řádkování. Zkusíš vysvětlit, proč tomu tak je?

Řešení

Ideální způsob, jak odřádkování spravit, je odstranit z konce řetězce bílé znaky (mezery a nové řádky) pomocí metody rstrip:

print('Slyšela jsem tuto básničku:')
print()

with open('basnicka.txt', encoding='utf-8') as soubor:
    for radek in soubor:
        radek = radek.rstrip()
        print('    ' + radek)

print()
print('Jak se ti líbí?')

Psaní souborů

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 soubor otevři s pojmenovaným argumentem mode='w' (z angl. mode, mód a write, psát).

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š.

Informace pak do souboru zapiš známou funkcí print, a to s pojmenovaným argumentem file:

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:2019/plzen-jaro-2019:exrcs_fio:0",
      "title": "Soubory",
      "html": "\n          \n    \n\n    <h1>Soubory</h1>\n<p>Dnes se pod&#xED;v&#xE1;me na to, jak v Pythonu &#x10D;&#xED;st z\n(a pak i zapisovat do) soubor&#x16F;.</p>\n<p>Vytvo&#x159; si v&#xA0;editoru soubor <code>basnicka.txt</code> a napi&#x161; do n&#x11B;j libovolnou b&#xE1;sni&#x10D;ku.\nSoubor ulo&#x17E;.</p>\n<div class=\"admonition note\"><p>Na ulo&#x17E;en&#xED; souboru s b&#xE1;sni&#x10D;kou doporu&#x10D;uji pou&#x17E;&#xED;t\nstejn&#xFD; editor, jak&#xFD; pou&#x17E;&#xED;v&#xE1;&#x161; na Pythonn&#xED; programy.</p>\n<p>Pou&#x17E;&#xED;v&#xE1;&#x161;-li jin&#xFD; editor ne&#x17E; Atom, dej si p&#x159;i ukl&#xE1;d&#xE1;n&#xED; pozor na k&#xF3;dov&#xE1;n&#xED;:</p>\n<ul>\n<li>Nab&#xED;z&#xED;-li ti editor p&#x159;i ukl&#xE1;d&#xE1;n&#xED; v&#xFD;b&#x11B;r k&#xF3;dov&#xE1;n&#xED;, vyber UTF-8.</li>\n<li>Je-li k dispozici k&#xF3;dov&#xE1;n&#xED; &#x201E;UTF-8 bez BOM&#x201D;, pou&#x17E;ij to.</li>\n<li>Pokud mus&#xED;&#x161; pou&#x17E;&#xED;t Notepad, kter&#xFD; v&#xFD;&#x161;e uveden&#xE9; mo&#x17E;nosti nem&#xE1;, pak v&#xA0;k&#xF3;du\nn&#xED;&#x17E;e pou&#x17E;ij m&#xED;sto <code>&apos;utf-8&apos;</code> nestandardn&#xED; <code>&apos;utf-8-sig&apos;</code>.</li>\n</ul>\n<p>Ono <a href=\"https://en.wikipedia.org/wiki/UTF-8\"><code>utf-8</code></a> je n&#xE1;zev standardn&#xED;ho k&#xF3;dov&#xE1;n&#xED;.\nZaji&#x161;&#x165;uje, &#x17E;e se p&#x159;&#xED;padn&#xE9; emoji nebo znaky s&#xA0;diakritikou do souboru ulo&#x17E;&#xED;\ntak, aby se daly p&#x159;e&#x10D;&#xED;st i na jin&#xE9;m po&#x10D;&#xED;ta&#x10D;i &#x10D;i opera&#x10D;n&#xED;m syst&#xE9;mu.\n&#x1F389;</p>\n</div><p>Potom napi&#x161; 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\">&apos;basnicka.txt&apos;</span><span class=\"p\">,</span> <span class=\"n\">encoding</span><span class=\"o\">=</span><span class=\"s1\">&apos;utf-8&apos;</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=\"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\">obsah</span><span class=\"p\">)</span>\n</pre></div><p>a spus&#x165; ho z adres&#xE1;&#x159;e, ve kter&#xE9;m je\n<code>basnicka.txt</code> (jin&#xFD;mi slovy, aktu&#xE1;ln&#xED; adres&#xE1;&#x159; mus&#xED; b&#xFD;t ten, kter&#xFD;\nobsahuje soubor s&#xA0;b&#xE1;sni&#x10D;kou).</p>\n<p>Obsah souboru se vyp&#xED;&#x161;e!</p>\n<p>Co se tu d&#x11B;je?\nTak jako <code>int()</code> vrac&#xED; &#x10D;&#xED;sla a <code>input()</code> &#x159;et&#x11B;zce, funkce\n<code>open()</code> vrac&#xED; hodnotu, kter&#xE1; p&#x159;edstavuje <em>otev&#x159;en&#xFD; soubor</em>.\nTahle hodnota m&#xE1; vlastn&#xED; metody.\nTady pou&#x17E;&#xED;v&#xE1;me metodu <code>read()</code>, kter&#xE1;\nnajednou p&#x159;e&#x10D;te cel&#xFD; obsah souboru a vr&#xE1;t&#xED; ho jako &#x159;et&#x11B;zec.\nNakonec metoda <code>close()</code> otev&#x159;en&#xFD; soubor zase zav&#x159;e.</p>\n<h2>Automatick&#xE9; zav&#xED;r&#xE1;n&#xED; soubor&#x16F;</h2>\n<p>Soubory se daj&#xED; p&#x159;irovnat k ledni&#x10D;ce: abys n&#x11B;co\nmohla z ledni&#x10D;ky vz&#xED;t, nebo d&#xE1;t dovnit&#x159;, mus&#xED;&#x161;\nji p&#x159;edt&#xED;m otev&#x159;&#xED;t a potom zav&#x159;&#xED;t.\nBez zav&#x159;en&#xED; to sice na prvn&#xED; pohled funguje taky,\nale pravd&#x11B;podobn&#x11B; potom brzo n&#x11B;co zplesniv&#xED;.</p>\n<p>Stejn&#x11B; tak je docela d&#x16F;le&#x17E;it&#xE9; soubor zav&#x159;&#xED;t po tom,\nco s&#xA0;n&#xED;m p&#x159;estane&#x161; pracovat.\nBez zav&#x159;en&#xED; to na prvn&#xED; pohled funguje, ale slo&#x17E;it&#x11B;j&#x161;&#xED; programy se m&#x16F;&#x17E;ou dostat\ndo probl&#xE9;m&#x16F;.\nOpera&#x10D;n&#xED; syst&#xE9;my maj&#xED; limity na po&#x10D;et\nsou&#x10D;asn&#x11B; otev&#x159;en&#xFD;ch soubor&#x16F;, kter&#xE9; se nezav&#xED;r&#xE1;n&#xED;m\ndaj&#xED; snadno p&#x159;ekro&#x10D;it.\nNa Windows nav&#xED;c nem&#x16F;&#x17E;e&#x161; soubor, kter&#xFD; je st&#xE1;le\notev&#x159;en&#xFD;, otev&#x159;&#xED;t znovu.</p>\n<p>Na korektn&#xED; zav&#x159;en&#xED; souboru ale program&#xE1;to&#x159;i &#x10D;asto zapomenou.\nProto Python poskytuje p&#x159;&#xED;kaz <code>with</code>, kter&#xFD; soubory zav&#xED;r&#xE1; automaticky.\nPou&#x17E;&#xED;v&#xE1; se takhle:</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\">&apos;basnicka.txt&apos;</span><span class=\"p\">,</span> <span class=\"n\">encoding</span><span class=\"o\">=</span><span class=\"s1\">&apos;utf-8&apos;</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\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"n\">obsah</span><span class=\"p\">)</span>\n</pre></div><p>P&#x159;&#xED;kaz <code>with</code> vezme otev&#x159;en&#xFD; soubor (kter&#xFD; vrac&#xED; funkce <code>open</code>)\na p&#x159;i&#x159;ad&#xED; ho do prom&#x11B;nn&#xE9; <code>soubor</code>.\nPak n&#xE1;sleduje odsazen&#xFD; blok k&#xF3;du, kde se souborem m&#x16F;&#x17E;e&#x161; pracovat &#x2013; v&#xA0;tomhle\np&#x159;&#xED;pad&#x11B; pomoc&#xED; metody <code>read</code> p&#x159;e&#x10D;&#xED;st obsah jako &#x159;et&#x11B;zec.\nKdy&#x17E; se Python dostane na konec odsazen&#xE9;ho bloku, soubor automaticky zav&#x159;e.</p>\n<p>V naprost&#xE9; v&#x11B;t&#x161;in&#x11B; p&#x159;&#xED;pad&#x16F; je pro otev&#xED;r&#xE1;n&#xED; soubor&#x16F; nejlep&#x161;&#xED; pou&#x17E;&#xED;t <code>with</code>.</p>\n<h2>Iterace nad soubory</h2>\n<p>Otev&#x159;en&#xE9; soubory se, jako nap&#x159;. &#x159;et&#x11B;zce &#x10D;i <code>range</code>,\ndaj&#xED; pou&#x17E;&#xED;t s p&#x159;&#xED;kazem <code>for</code>.\nTak jako <code>for i in range</code> poskytuje za sebou jdouc&#xED; &#x10D;&#xED;sla a <code>for c in &apos;abcd&apos;</code>\nposkytuje jednotliv&#xE9; znaky &#x159;et&#x11B;zce, <code>for radek in soubor</code> bude do prom&#x11B;nn&#xE9;\n<code>radek</code> d&#xE1;vat jednotliv&#xE9; &#x159;&#xE1;dky &#x10D;ten&#xE9; ze souboru.</p>\n<p>Nap&#x159;&#xED;klad m&#x16F;&#x17E;e&#x161; b&#xE1;sni&#x10D;ku odsadit,\naby se vyj&#xED;mala v textu:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;Sly&#x161;ela jsem tuto b&#xE1;sni&#x10D;ku:&apos;</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">()</span>\n\n<span class=\"k\">with</span> <span class=\"nb\">open</span><span class=\"p\">(</span><span class=\"s1\">&apos;basnicka.txt&apos;</span><span class=\"p\">,</span> <span class=\"n\">encoding</span><span class=\"o\">=</span><span class=\"s1\">&apos;utf-8&apos;</span><span class=\"p\">)</span> <span class=\"k\">as</span> <span class=\"n\">soubor</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\">&apos;    &apos;</span> <span class=\"o\">+</span> <span class=\"n\">radek</span><span class=\"p\">)</span>\n\n<span class=\"k\">print</span><span class=\"p\">()</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;Jak se ti l&#xED;b&#xED;?&apos;</span><span class=\"p\">)</span>\n</pre></div><p>Kdy&#x17E; to zkus&#xED;&#x161;, zjist&#xED;&#x161;, &#x17E;e trochu nesed&#xED;\n&#x159;&#xE1;dkov&#xE1;n&#xED;. Zkus&#xED;&#x161; vysv&#x11B;tlit, pro&#x10D; tomu tak je?</p>\n<div class=\"solution\" id=\"solution-0\">\n    <h3>&#x158;e&#x161;en&#xED;</h3>\n    <div class=\"solution-cover\">\n        <a href=\"/2019/plzen-jaro-2019/beginners/files/index/solutions/0/\"><span class=\"link-text\">Uk&#xE1;zat &#x159;e&#x161;en&#xED;</span></a>\n    </div>\n    <div class=\"solution-body\" aria-hidden=\"true\">\n        <p>Ka&#x17E;d&#xFD; &#x159;&#xE1;dek kon&#x10D;&#xED; znakem nov&#xE9;ho &#x159;&#xE1;dku, <code>&apos;\\n&apos;</code>,\nkter&#xFD; mo&#x17E;n&#xE1; zn&#xE1;&#x161; ze <a href=\"/2019/plzen-jaro-2019/beginners/str/\">sekce o &#x159;et&#x11B;zc&#xED;ch</a>.\nP&#x159;i proch&#xE1;zen&#xED; souboru Python tento znak nech&#xE1;v&#xE1; na konci &#x159;et&#x11B;zce <code>radek</code> &#xB9;.\nFunkce <code>print</code> pak p&#x159;id&#xE1; dal&#x161;&#xED; nov&#xFD; &#x159;&#xE1;dek, proto&#x17E;e ta na konci\nv&#xFD;pisu v&#x17E;dycky od&#x159;&#xE1;dkov&#xE1;v&#xE1; &#x2013; pokud nedostane argument <code>end=&apos;&apos;</code>.</p>\n<hr>\n<p>&#xB9; Pro&#x10D; to d&#x11B;l&#xE1;? Kdyby <code>&apos;\\n&apos;</code> na konci &#x159;&#xE1;dk&#x16F; nebylo,\nnedalo by se nap&#x159;. dob&#x159;e rozli&#x161;it, jestli posledn&#xED; &#x159;&#xE1;dek\nkon&#x10D;&#xED; na <code>&apos;\\n&apos;</code></p>\n    </div>\n</div><p>Ide&#xE1;ln&#xED; zp&#x16F;sob, jak od&#x159;&#xE1;dkov&#xE1;n&#xED; spravit, je odstranit z&#xA0;konce &#x159;et&#x11B;zce\nb&#xED;l&#xE9; znaky (mezery a nov&#xE9; &#x159;&#xE1;dky) pomoc&#xED; metody <code>rstrip</code>:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;Sly&#x161;ela jsem tuto b&#xE1;sni&#x10D;ku:&apos;</span><span class=\"p\">)</span>\n<span class=\"k\">print</span><span class=\"p\">()</span>\n\n<span class=\"k\">with</span> <span class=\"nb\">open</span><span class=\"p\">(</span><span class=\"s1\">&apos;basnicka.txt&apos;</span><span class=\"p\">,</span> <span class=\"n\">encoding</span><span class=\"o\">=</span><span class=\"s1\">&apos;utf-8&apos;</span><span class=\"p\">)</span> <span class=\"k\">as</span> <span class=\"n\">soubor</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=\"n\">radek</span> <span class=\"o\">=</span> <span class=\"n\">radek</span><span class=\"o\">.</span><span class=\"n\">rstrip</span><span class=\"p\">()</span>\n        <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;    &apos;</span> <span class=\"o\">+</span> <span class=\"n\">radek</span><span class=\"p\">)</span>\n\n<span class=\"k\">print</span><span class=\"p\">()</span>\n<span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s1\">&apos;Jak se ti l&#xED;b&#xED;?&apos;</span><span class=\"p\">)</span>\n</pre></div><h2>Psan&#xED; soubor&#x16F;</h2>\n<div class=\"admonition warning\"><p class=\"admonition-title\">Pozor!</p>\n<p>Pro Python nen&#xED; probl&#xE9;m smazat obsah jak&#xE9;hokoli souboru.\nPsan&#xED; do soubor&#x16F; si zkou&#x161;ej v&#xA0;adres&#xE1;&#x159;i, ve kter&#xE9;m nem&#xE1;&#x161; ulo&#x17E;en&#xE9;\nd&#x16F;le&#x17E;it&#xE9; informace!</p>\n</div><p>Soubory se v&#xA0;Pythonu daj&#xED; i zapisovat.\nPro z&#xE1;pis soubor otev&#x159;i s pojmenovan&#xFD;m\nargumentem <code>mode=&apos;w&apos;</code> (z&#xA0;angl. <em>mode</em>, m&#xF3;d a <em>write</em>, ps&#xE1;t).</p>\n<p>Pokud soubor u&#x17E; existuje, otev&#x159;en&#xED;m s&#xA0;<code>mode=&apos;w&apos;</code> se ve&#x161;ker&#xFD; jeho obsah sma&#x17E;e.\nPo zav&#x159;en&#xED; tak v&#xA0;souboru bude jen to, co do n&#x11B;j ve sv&#xE9;m programu zap&#xED;&#x161;e&#x161;.</p>\n<p>Informace pak do souboru zapi&#x161; zn&#xE1;mou funkc&#xED; <code>print</code>,\na to s&#xA0;pojmenovan&#xFD;m argumentem <code>file</code>:</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\">&apos;druha-basnicka.txt&apos;</span><span class=\"p\">,</span> <span class=\"n\">mode</span><span class=\"o\">=</span><span class=\"s1\">&apos;w&apos;</span><span class=\"p\">,</span> <span class=\"n\">encoding</span><span class=\"o\">=</span><span class=\"s1\">&apos;utf-8&apos;</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\">&apos;Na&#x161;e star&#xE9; hodiny&apos;</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\">&apos;Bij&#xED;&apos;</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\">&apos;hodiny&apos;</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        "
    }
  }
}