Hogyan fordítsuk meg nagyméretű fájlok sorainak sorrendjét?

Bizonyos esetekben szükséges vagy előnyös lehet, hogy fájlban tárolt adatsorok sorrendjét a feldolgozásuk előtt megfordítsuk (a fájlba írt utolsó sort lehessen elsőként beolvasni). Ilyen igény merülhet fel például naplófájlok vagy más idősoros adatok (pl. tőzsdei árfolyamok) feldolgozásakor, amikor is a jelenhez időben közelebbi (de a fájlban az utolsók között szereplő) adatok érdekesebbek lehetnek, mint a régebbiek.

A kérdés az, hogy mi van akkor, ha olyan nagy adathalmazzal (nagyon sok sorból álló fájlal) kell dolgozni, amely már nem tölthető be egyszerre a memóriába, és így pl. a reversed() beépített függvény nem alkalmazható?

Hasonló problematikával már foglalkoztunk a „Nagyméretű adathalmazok rendezése” című bejegyzésben, ahol az alkalmazott megoldás az úgynevezett külső rendezés volt. Ennek alapelvét a sorrendfordítás esetén is használhatjuk, amit az alábbi ábra szemléltet.

A módszernek most is két fázisa van, amelyek főbb lépései a következők:

1. Fázis:  Darabolás és ideiglenes sorrendfordítás

    • A megfordítandó szövegfájlból egymás után beolvassuk a sorokat, de egyszerre csak annyit, hogy ezek bájtban mért összmérete ne haladja meg az általunk előzetesen megszabott korlátot (pl. 40 MB). A sorok bájtban mért méretét úgy határozzuk meg, hogy megnézzük, hogy a sor hány karakterből áll, és e számot megszorozzuk 4 bájttal. Ennek indoka, hogy az UTF-8 maximum 4 bájton kódol egy karaktert. Ezzel ugyan felülbecsüljük a méretet, de ez a művelet sokkal gyorsabb, mintha a tényleges bájtokat számolnánk az encode() metódussal, vagy a valós memóriafoglaltságot számoltatnánk a sys modul getsizeof() függvényével.
    • Minden alkalommal, amikor a megfelelő méretű adatsor-szakasz előáll, akkor azt egy listában eltároljuk, és e lista elemeit helyben megfordítjuk a reverse() metódussal.
    • A fordított elemsorrendű lista tartalmát egy egyedi névvel rendelkező ideiglenes fájlba mentjük. A fájlok nevét a mentések sorrendjében egy listában eltároljuk.
      Az ideiglenes fájlok egy ideiglenes mappában gyűlnek.

    2. Fázis:  Összefűzés fordított sorrendben

    • Miután az összes ideiglenes fájl létrejött, azokat sorban egymás után megnyitjuk a mentési sorrendhez képest fordított sorrendben (tehát az utolsóként mentett ideiglenes fájlt olvassuk be először) és tartalmukat kiírjuk egy végső kimeneti eredményfájlba. Ez a fájl tehát a kiinduló, eredeti fájl sorait tartalmazza, de fordított sorrendben.
    • Az ideiglenes mappát a tartalmával együtt eltávolítjuk.

    E lépések programmal történű megvalósítása mind függvénnyel, mind osztállyal megtehető. Mi most az utóbbit választjuk. A FileReverser osztály definícióját, valamint a tesztsorokat láthatjuk alább. A működés megértését a részletes kommentek és az előző elvi ábra segíti.

    Annak, aki jobban el akar mélyülni e feladatban, érdemes lehet gyakorlásképpen a fájltartalom sorrendfordítását függvénnyel is megvalósítani, és annak definícióját megalkotni.

    E bejegyzésben elsődlegesen a fájlkezelés és az ideiglenes fájlokkal és könyvtárakkal való munka voltak a középpontban. Ezekkel a Python tudásépítés lépésről lépésre című e-könyv „Mentsük, ami menthető! – fájlok és mappák” fejezete foglalkozik részletesen.

    Érdekel a Python tudásépítés lépésről lépésre az alapoktól az első asztali alkalmazásig című e-könyv.