<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/cjs/screen.xsl" media="screen"?>
<lecture>

<meta>
  <maintitle>Python</maintitle>
  <author>Jiří Znamenáček</author>
  <title>„fileinput“ – vícenásobný vstup</title>
  <date>2018-03-09</date>
  <link><!--a href="http://vyuka.ookami.cz" rel="external">http://vyuka.ookami.cz</a--></link>
  <!--
  <style>
    /* CSSka */
  </style>
  <use-math/>
  -->
</meta>
<!--
  „“–…
  ←→ ↑↓ ↔↕
  ↵ aneb &#x21B5; aneb \r aneb CR aneb CarriageReturn
-->


<slide title="Úvod">

    <p>
        Klasická funkce <a href="overview.xml"><code>open()</code></a> otevírá přístup právě k jednomu fyzickému souboru, ať už textovému či binárnímu. Knihovní funkce <strong><code>fileinput.input()</code></strong> je jejím velmi výrazným zobecněním (převážně pro čtení dat, ale <a href="?slajd=3">viz parametr <em>inplace</em></a>), protože umožňuje:
    </p>
    <ul>
        <li>
            otevřít více souborů postupně za sebou;
        </li>
        <li>
            číst data ze standardního vstupu (tedy třebas i z <em>pajpy</em>!).
        </li>
    </ul>
    <p>
        Navíc dokáže automaticky zpracovat soubory komprimované pomocí <em>gzip</em> (přípona <em>.gz</em>) a <em>bzip2</em> (přípona <em>.bz2</em>), jakož i poradit si s chybami v kódování.
    </p>
    <p>
        Kromě toho je solidně konfigurovatelná a je možno si dopsat vlastní handlery pro další typy souborů.
    </p>

</slide>
<slide title="Základní použití">

    <p>
        V úplně nejjednodušší podobě stačí napsat:
    </p>
    <example lang="python">
        import fileinput

        with fileinput.input() as f:
            for line in f:
                print(line)
    </example>
    <p>
        Blíže neurčený vstup je pak zpracován následujícím způsobem:
    </p>
    <ul>
        <li>
            následují-li za skriptem jako parametry příkazové řádky soubory (tj. v <code>sys.argv[1:]</code>), jsou postupně čteny řádku po řádce jedem po druhém;
        </li>
        <li>
            nejsou-li takto zadány – nebo-je li zadán znak <code>'-'</code> – žádné soubory, jsou řádky načítány ze standardního vstupu <code>sys.stdin</code> (tedy z <em>pajpy</em>).
        </li>
    </ul>
    <p>
        Výchozí nastavení je stejné jako u funkce <em>open()</em>, tj. <strong>textové soubory pro čtení (<em>'r'</em>)</strong>, přičemž jediný další povolený způsob je binární soubor pro čtení (<em>'rb'</em>). Nicméně je možné <a href="?slajd=3">otevřené soubory přepisovat</a>, případně ukládat záložní kopie.
    </p>
    <note>
        PS: Můžete samozřejmě použít i klasické otevření bez <em>with</em>-kontextu..
        <example lang="python">
            for řádka in fileinput.input():
                BLOK
        </example>
        ..ale pak si musíte pohlídat uzavření proudů plus všechny chyby a výjimky sami.
    </note>

</slide>
<slide title="Plný konstruktor">

    <p>
        Plný konstruktor funkce <em>input()</em> odhaluje, že toho lze nastavit mnohem více:
    </p>
    <blockquote>
        fileinput.input(files=None, inplace=False, backup='', mode='r', openhook=None)
    </blockquote>
    <p>
        Především tedy můžete:
    </p>
    <ul>
        <li>
            zadat soubory k otevření pomocí parametru <em>files</em> (typicky jako n-tice nebo seznam řetězců);
        </li>
        <li>
            změnit způsob práce s proudy pomocí parametru <em>mode</em> (povolené jsou tedy pouze <em>'r'</em> a <em>'rb'</em>);
        </li>
        <li>
            přesměrovat standardní výstup do právě otevřeného souboru nastavením <em>inplace=True</em>, přičemž nastavením <em>backup='.přípona'</em> zachováte originál v příslušně pojmenované záloze;
            <note>
                Pro vstup ze standardního vstupu toto samozřejmě nejde použít.
            </note>
        </li>
        <li>
            provést s otevíranými soubory <a href="?slajd=4">dodatečnou operaci</a> na základě hodnoty parametru <em>openhook</em>.
            <note>
                Prozměnu zase pochopitelně nejde kombinovat s parametrem <em>inplace</em>.
            </note>
        </li>
    </ul>

</slide>
<slide title="Parametr „openhook“">

    <p>
        K dispozici jsou dva připravené <em>openhook</em>y:
    </p>
    <ul>
        <li>
            <code>fileinput.hook_compressed</code> dokáže otevřít soubory zkomprimované pomocí metod <em>gzip</em> (pouze přípona <em>.gz</em>!), respektive <em>bzip2</em> (pouze přípona <em>.bz2</em>!);
        </li>
        <li>
            <code>fileinput.hook_encoded</code> předepisuje kódování pro otevírané textové soubory a <a href="texts.xml?slajd=11">způsob řešení chyb</a> při jejich dekódování zadaným algoritmem.
            <note>
                <example lang="python">
                    # příklad pro UTF-8 s přemapováním problematických znaků
                    fileinput.hook_encoded("utf-8", "surrogateescape")
                </example>
            </note>
        </li>
    </ul>
    <p>
        Kromě těchto dvou předpřipravených je možno si napsat vlastní. Jedná se totiž o funkce, které přebírají právě dva vstupní parametry – jméno/cestu k souboru a způsob jeho otevření (tedy <em>'r'</em>/<em>'rb'</em>) – a mají za úkol vrátit příslušným způsobem otevřený tento soubor.
    </p>
    <note>
        A jak je vidět z druhého <em>openhook</em>u, dekorátorům a fantazii se meze nekladou, takže si toho můžete navymýšlet docela dost.
    </note>

</slide>
<slide title="Funkce kontextu">

    <p>
        Jelikož primárním účelem modulu <em>fileinput</em> je načítání vícero proudů najednou, nepřekvapí, že máme k dispozici funkce, které nám v načítaných souborech pomohou udělat trochu pořádek:
    </p>
    <ul>
        <li>
            <code>filename()</code> a <code>fileno()</code> vrací jméno a „celočíselný deskriptor“ právě zpracovávaného souboru;
        </li>
        <li>
            <code>filelineno()</code>, resp. <code>lineno()</code> vrací pořadové číslo řádky v aktuálním souboru, resp. akumulovaně přes všechny dosud načtené řádky ze všech již zpracovaných souborů;
        </li>
        <li>
            <code>isfirstline()</code> vrací <em>True</em>, pokud právě zpracovávaná řádka je první řádkou aktuálního souboru;
        </li>
        <li>
            <code>isstdin()</code> vrací <em>True</em> jen a pouze v tom případě, kdy poslední načtená řádka pochází ze <em>sys.stdin</em>;
        </li>
        <li>
            <code>nextfile()</code> uzavírá aktuální otevřený proud a přechází na další (případné nedočtené řádky se do celkových nepřekvapivě nezapočítávají);
            <note>
                Tato funkce je v provozu až po načtení první řádky souboru, takže ji nemůžete použít kupříkladu pro přeskočení prvního z proudů.
            </note>
        </li>
        <li>
            <code>close()</code> uzavírá kontext (nezávisle na tom, zda byl již vyčerpán nebo ne).
        </li>
    </ul>

</slide>


</lecture>
