Az első Python-projektem: hogyan alakítottam át egy rendezetlen szövegfájlt egy csinos CSV-fájlba

Ezért úgy döntöttem, hogy megtanulom a Pythont. Kiderült, hogy ez a számítógépes programozási nyelv nem olyan nehéz (nos, amíg nem kaptam meg ezt a projektet!: P).

Másodperceken belül beleszerettem a könnyű, éles szintaxisba és az automatikus behúzásba írás közben. Megigézett, amikor megtudtam, hogy olyan adatstruktúrákat, mint listák, sorrendek és szótárak, dinamikusan lehet létrehozni és inicializálni egyetlen sorral (például listanév = []).

Sőt, az ezekben tárolt értékek indexek használatával és anélkül is elérhetők voltak. Ez a kódot jól olvashatóvá teszi, mivel az index helyébe egy választott angol szó lép.

Nos, elég sokat mondtak a nyelvről. Hadd mutassam meg, mit követelt a projekt.

A bátyám adta nekem ezt a projektet. Több ezer szót tartalmazó szövegfájlra bukkant. Sok szónak szinte ugyanaz a jelentése. Mindegyik szó mellett volt egy definíció és egy példamondat, de nem annyira szervezetten. Szó és új vonal volt a szó és a mondata között. Néhány szempont hiányzott a szavakból. Az alábbiakban bemutatom a szöveges fájl részleteit, amelyekről beszélek:

Azt akarta, hogy a szöveg szempontjai egységesek legyenek. Ehhez arra volt szüksége, hogy szépen válogassam össze az összes hasonló jelentésű szót egy téma mellett. Azt mondta nekem, hogy ez úgy érhető el, hogy a szövegben szereplő összes adatot a következő formátumú szótárba foglalja:

majd beírja őket egy CSV (vesszővel elválasztott értékek) fájlba.

Megkérdezte, hogy ezt felvehetném-e első projektként, most, hogy megtanultam az alapokat. Nagyon örültem a logika kidolgozásának, ezért azonnal beleegyeztem. Amikor a határidőre kérdeztek, tisztességes, 2 napos időt adott a befejezésre.

Jaj, végül kettős időt vettem igénybe, mert megpróbáltam az írott kódot megfelelően hibakeresni. Őszintén szólva, ha nem a bátyám rövid látogatásai a szobámba nézték volna az előrehaladást és utaltak volna a feltételezések megfogalmazása során téves feltételezésekre, akkor az a célom volt, hogy az örökkévalóságban befejezzem: P

Azzal kezdtem, hogy mini programokat hoztam létre a programon belül, és amelyeket a teljes program felépítése előtt be akartam fejezni. Ezek az alábbiak voltak:

1. Regex formálása egy szám és a mellette lévő szó egyezésére.

Megvizsgáltam a szöveges fájlt, és észrevettem, hogy minden témának (a továbbiakban: kulcsnak nevezzük) előtte van egy szám. Szóval írtam néhány sor kódot a minta regexének (reguláris kifejezés - a szöveg kibontásának hatékony eszköze) elkészítéséhez az alábbiak szerint:

Amikor azonban ezt lefuttattam, pontosabban egy UnicodeDecodeError hibát kaptam, ami azt jelentette, hogy nincs hozzáférésem a szöveges fájlhoz. Utánanéztem a //stackoverflow.com oldalon, és hosszas keresés után, szerencsétlenül, a bátyám jött, és megoldást talált. A hibát a következőképpen javították:

Ennek ellenére nem kaptam meg a kívánt kimenetet. Ennek oka az volt, hogy egyes billentyűknek perjelek ('/') vagy szóközök ('') voltak a szövegben, amelyekhez a regexem nem volt képes. Gondoltam később javítani a regex kifejezésen, és írtam egy megjegyzést mellé.

2. Vonalak listájának megszerzése karakterláncként a szövegfájlból

Ehhez csak 1 kódsort írtam, és szerencsére nem jelentek meg hibák.

Tisztátalan listát szereztem. Új vonalakat ('\ n') és szóközöket ('') tartalmazott, majd a következőképpen kívántam finomítani a listát:

3. A szavak, jelentések és példamondatok külön kivonása és hozzáadása a megfelelő listákhoz.

Ezt messze a legnehezebb megtenni, mivel a megfelelő logikát és a mintafelismerés alapján történő megfelelő megítélést tartalmazta.

Érdekes módon a szövegfájl fölött átpillantva több mintát vettem észre. Minden szónak ugyanabban a sorban volt jelentése, amelyet '=' jel választ el. Valamennyi példát megelőzte a ':' jel és a 'Példa' kulcsszó.

Arra gondoltam, hogy újra felhasználom a regexet. Találtam egy alternatív és elegánsabb megoldást úgy, hogy a vonalat (most már egy karakterláncot a listában) szeleteltem a szimbólumok elhelyezkedése szerint. A szeletelés a python másik jó tulajdonsága. A kódot a következőképpen írtam:

A fenti kód szinte angolul olvasható. A tiszta lista minden sorához ellenőrzi, hogy van-e rajta '=' vagy ':' jel. Ha mégis, akkor a jel indexét megtalálja, és ennek megfelelően elvégzi a szeletelést.

Az első „if” -ben a „=” előtti részt a „word” változóban, az utána lévő részt pedig a „jelentése” -ben tároljuk. Hasonlóan a második „if” („elif - else if - ebben az esetben”) esetében a „:” utáni részt a „example” tárolja. És minden egyes iteráció után a szó, a jelentés és a példa mondat a megfelelő listákban tárolódik. Ily módon a teljes adat kinyerhető.

Eddig jó. De megjegyeztem, hogy a kibontást úgy kellett elvégezni, hogy az adott kulcs minden szavát (és szempontjait) együtt kell felhalmozni, mint a kulcs egyik értékét. Ez azt jelentette, hogy minden szót, jelentést és példát egy kettőben kellett tárolni. Minden egyes lemezt egyetlen listán kellett tárolni, amely egy adott kulcs értékeként jelentené magát. Ezt az alábbiakban mutatjuk be:

Ehhez azt terveztem, hogy minden kulcs minden szavát, jelentését és mondatát összegyűjtem egy külön listán, amelyet egy másik lista, mondjuk kulcs-lista zár. A kép megint pontosabban megmondja:

Ehhez hozzáadtam a következő kódot ahhoz a kódhoz, amelyet szeletelés céljából írtam:

Ennek a kódnak a logikája (a másik része) sajnos rossznak bizonyult. Tévesen feltételeztem, hogy csak 2 feltétel ('=' és ':') létezik a szövegben. Sok kivétel volt, amelyet nem vettem észre. Órákat vesztegettem a logika esetleges hibáinak elhárításáért. Feltételeztem, hogy a teljes szöveges fájl ugyanazt a mintát követi. De egyszerűen nem ez volt a helyzet.

Mivel nem tudtam előrelépni, a program következő részéhez léptem. Úgy gondoltam, hogy a többi rész elvégzése után igénybe vehetném a testvérem segítségét. : P

Folytatjuk…

4. Értékek létrehozása a kulcsokhoz a Zip Function és a Parameter kicsomagolás segítségével.

Ezen a ponton még a listák fenti konfigurációjának elérése után sem voltam teljesen biztos abban, mit tegyek. A bátyám egyik technikai beszélgetése során megtanultam a „Zip” funkciót és a „Parameter kicsomagolást”, amely szó szerint cipelte a hozzá átadott listákat, így:

Úgy gondoltam, hogy valahogy össze tudom kapcsolni ezt a két tulajdonságot a kívánt eredmény elérése érdekében. Egy kis toppolás után, a funkciók tesztelése és a dummy listákon való munka után sikerült. Ehhez a feladathoz külön fájlt (béta) hoztam létre, amelynek részletét az alábbiakban adjuk meg:

A fenti kód működését kitalálhatjuk, ha megnézzük a kimenetet:

A zip () függvény összehúzza a megfelelő listákat vagy értékeket a listákon belül, és bezárja őket egy kettőbe. A listák belsejében lévő sorokat ezután listákká alakítják kicsomagolás és további cipzárolás céljából. Végül megkapjuk a kívánt kimenetet.

Nagy megkönnyebbülést éreztem a kód működése miatt. Örültem, hogy manipulálni tudtam a kivonatolható adatokat, és a szükséges formátumba formálhattam. Másoltam a kódot a fő fájlba, amin dolgoztam, és ennek megfelelően módosítottam a változóneveket. Most már csak annyi maradt, hogy értékeket rendeltünk a szótár kulcsaihoz (és természetesen a kibontó részhez!).

5. Értékek hozzárendelése a szótár kulcsaihoz.

Ehhez a kóddal végzett némi kísérletezés után jutottam erre a megoldásra:

Ez a kívánt eredményt a következőképpen hozta létre:

A program majdnem elkészült. A fő probléma az adatkivonási részben volt.

… Folytatás a 3. szakaszból

Órák és órák hibakeresése után egyre jobban elkeseredtem, hogy miért nem működik az átkozott dolog. Felhívtam a bátyámat, ő pedig finom utalást adott azokra a feltételezésekre, amelyeket a feltételes hurkok és ha-más záradékok meghatározása közben tettem. Vizsgáltuk a szöveges fájlt, és észrevettük, hogy egyes szavaknak egy helyett két sorban vannak példái.

Kódlogikám szerint, mivel a második sorban nincs ':' jel (és ebben az esetben sem '=' jel), a sor tartalmát nem kezeljük a példa részeként. Ennek eredményeként ez a kijelentés igazsá tenné az utolsó „else” részt, és végrehajtaná a benne írt kódot. Mindezeket figyelembe véve az alábbiak szerint módosítottam a kódot:

Itt a hasNumbers () egy függvény, amely ellenőrzi, hogy egy adott sorban vannak-e számok. A következőképpen határoztam meg:

Ez azt jelenti, hogy összegyűjti a példa második sorát, ha az összes többi feltétel nem sikerül, egyesíti az első sorral, majd hozzáadja a megfelelő listát, mint korábban.

Csalódásomra ez nem sikerült, ehelyett hibát mutatott, miszerint az index a tartományon kívül esik. Megdöbbentem, mivel véleményem szerint minden kódsor logikailag helyesnek tűnt.

Órák órákig tartó őrültsége után megmutatta a módját, hogy lehívjam azokat a sorszámokat, ahol a hiba történt. A programozás egyik fő készsége a program hibakeresésének képessége, hogy megfelelően ellenőrizze az esetleges hibákat és fenntartsa a folyamatos folyamatot.

Érdekes módon a kód következő kiegészítése arról számolt be, hogy a hiba a szöveges fájl 1750-es sora körül következett be.

Ez azt jelentette, hogy a program jól működött a sorszámig, és hogy a kódom helyes volt! A problémák téves feltételezéseimben és a szövegfájlban rejlenek, heterogenitásának köszönhetően.

Ezúttal azt vettem észre, hogy néhány kulcs nem a számuk alapján történt, ami problémákat okozott a logikai folyamatban. Javítottam a hibákat a kód további módosításával az alábbiak szerint:

Ez jól működött a szöveges fájl 4428. soráig, de azonnal összeomlott. Megnéztem ezt a sorszámot magában a szövegfájlban, de ez nem sokat segített. Aztán rájöttem, nagy boldogságomra, hogy ez biztos az utolsó sor. Az egész program a tiszta listán dolgozott, amely nem tartalmazott új sorokat és szóközöket. Kinyomtattam a tiszta lista utolsó sorát, és összehasonlítottam a szövegfájl utolsó sorával. Egyeztek!

Rendkívül boldog voltam, amikor ezt tudtam, mivel ez azt jelentette, hogy a programot a végéig futtatták. Az egyetlen oka annak, hogy összeomlott, az volt, hogy az utolsó mondat után egyik kódnak sem volt értelme. A feltételeket úgy terveztem, hogy minden alkalommal ellenőrizzék a következő sort is, az aktuális sorral együtt. Mivel az utolsó vonal után nem volt vonal, lezuhant.

Szóval írtam egy további kódsort ennek fedezésére:

Most minden működött. Végül! Most már csak a kulcsokat kellett hozzárendelnem a megfelelő értékekhez, és ennyi! Ebben a pillanatban szünetet tartottam, tekintve, hogy a projektem végre véget ért. Később hozzáadnék néhány utolsó simítást.

De mielőtt szünetet tartanék, úgy döntöttem, hogy minden kódot különféle funkciókba csatolok, hogy a kód szép legyen. Már nagyon nehezen tudtam navigálni a kódsorokon felfelé és lefelé. Ezért úgy döntöttem, hogy egy kis szünetet tartok, miután ezt megtettem.

Ezt követően azonban a program változó hatókörű hibákat kezdett adni. Rájöttem, hogy ez azért van, mert a függvényekben deklarált változók nem hívhatók közvetlenül a függvényen kívülről, mivel a helyi névtérben vannak. Mivel nem akartam további változtatásokat végrehajtani a béna hiba miatt, úgy döntöttem, hogy visszatérek ugyanarra a kódra, amellyel kezdettől fogva a fejemet ütöttem.

Teljes hitetlenségemre azonban a program nem ugyanúgy működött, mint korábban. Valójában egyáltalán nem működött! Egyszerűen nem tudtam kideríteni az okát (és még mindig nem tudom!). A nap további részében teljesen depressziós voltam. Olyan volt, mint egy rémálom átélése még elalvás előtt!

Szerencsére és csodával határos módon a kód másnap működött, miután gondosan változtattam. Gondoskodtam róla, hogy sok béta fájlt készítsek (minden egyes módosításhoz), hogy elkerüljem az ilyen felesleges káoszt.

Néhány óra múlva végre befejezhettem a programomat (de csak addig, amíg el nem fogyasztottam 4 teljes napot). Végeztem még néhány változtatást, például:

i) a „hasNumbers” függvény módosítása „hasNumbersDot” függvényre, és kizárva a programban korábban készített regexet. Ez hatékonyabban illeszkedett a kulcsokhoz, mivel nem voltak feltételezések és ezért nem voltak kivételek. A kód a következő:

ii) a regex feltétel és a kulcsok cseréje a tiszta listából.

iii) az „ha” feltételek kombinálása a „példák kinyerése” részben

iv) a szótárkulcs-hozzárendelés kódjának materializálása

Ezenkívül némi próba és hiba után sikerült átalakítani a kapott adatokat egy gyönyörűen strukturált CSV fájlba:

Megtekintheti a profilomon található github adattáramat, ahol megtekintheti a program teljes kódját, beleértve a szöveges fájlt és a csv fájlt is.

Összességében nagy élmény volt. Ennyit meg kellett tanulnom ebből a projektből. Nagyobb bizalmat szereztem a képességeimben is. Néhány szerencsétlen esemény ellenére (a programozás ilyesmivel jár: P) végül végre tudtam hajtani az adott feladatot.

Egy utolsó dolog! Nemrég egy vidám mémre bukkantam a hibakeresés szakaszai kapcsán, amely annyira hasonlít a tapasztalataimhoz, hogy nem tudok ellenállni a megosztásnak. xD

Köszönöm, hogy egészen ideig elkészítetted (még akkor is, ha a legtöbbet kihagytad, hogy megnézhesd a végeredményt: P).