A regexről 300-500 oldalas könyveket lehet írni, és ennél a feladatnál nem is kell az összes szintaktikai elemét ismerned. Szóval egyelőre oldjuk meg ezt, aztán a többivel ráérsz később megbirkózni.
Az import/export tools ilyen szerkezetű html-eket gyárt:
<body>
<table border=0 cellspacing=0 cellpadding=0 width="100%" class="header-part1">
<tr>
<td>
<div class="headerdisplayname" style="display:inline;">Tárgy: </div>Levél tárgya
</td>
</tr>
[…]
</table>
<div class="moz-text-html" lang="x-unicode">
[…]
Nem ilyen szépen formázza meg, hanem egy sorba teszi a fejléctáblát, de ez (jelen esetben) nem számít. A lényeg, hogy a
<body>
és az első
<div class="moz-text-html"
szövegrész között lévő bármilyen dolgot kell neked eltávolítanod. Ha sikerül így megfogalmaznod a feladatot, akkor jön csak a regex.
Vannak barátságos online regex tesztelő oldalak, mint pl. a
regexpal.com (JavaScript),
rubular.com (Ruby),
gskinner.com/RegExr (Flash), amik azonnal mutatják, hogy a mintád mit talál meg a szövegben. Azért írtam utána, hogy miben készült, mert lehet eltérés a működésükben, főleg a több soros találatok kezelésében. (Érdemes a rubular.com-ot használni, az többé kevésbé Perl kompatibilis.) Megnyitod bármelyik tesztelő oldalt, a nagyméretű szövegdobozba bevágod a levél HTML kódját, a kisméretű szövegdobozban pedig elkezdhetsz kísérletezni.
- Először írd, be hogy
<body>
, és meg fogják találni, azt hogy <body>
.
- A
<body>
után írj egy pipe karaktert= | (AltGr+W), és utána kezd el gépelni a <div class="moz-text-html"
szövegrészt. Ahogy gépeled be, úgy fogod látni, hogy karakterenként miket talál még meg. Az álló vonal (pipe karakter) a „vagy”-ot jelenti a regexpben. Tehát most vagy a <body>
vagy a <div class="moz-text-html"
szövegeket jeleníti meg, és ezek között foglal helyet a törölni kívánt tábla. Így néz ki most a mintád:
<body>|<div class="moz-text-html"
Fontos: azért tudjuk ilyen egyszerűen beírni ezt a kettő határt, mert nincsen bennük a regex számára értelmezhető karakter. Ha lenne benne olyan karakter, akkor ezt le kellene védeni egy \ (repjel, backslash) karakterrel.
- Most az álló vonal helyére be kell írnunk, hogy minden egyéb karakter, amíg az első
<div class="moz-text-html"
részhez nem ér. Ezt az első vagy nem első dolgot regexben úgy hívjuk, hogy greedy (kapzsi) vagy non-greedy (nem kapzsi).
A minden karakter, kivéve a sortörés jele regexben a pont = . Ez nekünk még nem elég, meg kell adni, hogy hány darab „minden karakter, kivéve a sortörés” csoportba eső karaktert szeretnénk megtalálni. Ezeket hívjuk multiplier-nek (többszöröző). Most a feladatban elég csak egyet ismerni, az „egy vagy több” jelet, ami a plusz = +. (Használhatnánk a csillagot = * is, ami a „nulla, egy vagy több” jele, de ha a két határ között nulla egyéb szöveg van, akkor nincs fejléc, és minek dolgozunk vele).
A regex alapból kapzsi, mert ha belegondolsz, akkor a <div class="moz-text-html"
szövegrészre is ráillik az, hogy minden karakter, kivéve a sortörés, ezért ha többször fordul elő ugyanilyen szövegrész a szövegben, akkor csak az utolsónál fog megállni (mint határ), a többit kapzsi módon kijelöli. Ezért kell a többszöröző után még egy kérdőjel = ?, ami „nulla vagy egy jele” alapból, de ha más többszöröző mögött áll, akkor kikapcsolja a kapzsi működést.
Itt tartunk:
<body>.+?<div class="moz-text-html"
és itt jönnek elő a különböző értelmezők közötti különbségek. A rubular.com-on elég a jobb oldali kis szövegmezőbe beírni az „m” módosítót (m = make dot match new lines). A gskinner.com/RegExr oldalon be kell pipálnunk az „s” módosítót (ugyanaz, mint ruby-nál az m, csak más a neve). A regexpal.com-on viszont ezek nem működnek, mert az JavaScript. Különösebb magyarázat nélkül JavaScript-ben így kell:
<body>(.|[\r\n])+?<div class="moz-text-html"
Úgysem JavaScriptben, hanem Perlben lesz a végső megoldás.
- Eddig szép és jó, de most a határok is ki vannak jelölve, és ha azokat is törölnéd, akkor hibás lenne a HTML fájl. Ezért csoportokkal kell szűkíteni az egyes részeket. Itt már jó, ha gondolsz arra is, hogy neked nem a fejléc kell, hanem a fejléc nélküli összes többi rész, ezért érdemes inkább azokat csoportosítani.
A csoportosítás a kerek zárójelekkel történik = (). A zárójelben helyet foglaló mintára illeszkedő tartalom lesz majd a csoport tartalma. Mivel mi a fejlécen kívül minden egyebet szeretnénk csoportosítani, ezért a következő módon kell kiegészítenünk a mintát:
Először a szöveg elejétől a
<body>
szövegrész végéig kell nekünk mindent csoportosítani. A szöveg elejének jele regexben a kalap (angolul caret): ^ (AltGr+3). A „minden karakter kivéve sortörés” típust az előző pontban megbeszéltük: ennek alapján az első csoportosítás így néz ki:
(^.+?<body>).+?<div class="moz-text-html"
Most következik a második csoportosítás, amiben a <div class="moz-text-html"
kezdetétől a szöveg végéig kell mindent csoportba foglalnuk. A szöveg vége jele regexben a dollár: $ (AltGr+É). Hasonlóan az előzőhöz (csak itt nem kell a kapzsi módot kikapcsolni, mert szöveg vége biztosan csak egy van):
(^.+?<body>).+?(<div class="moz-text-html".+$)
Ha ezt az utolsó mintát beírod a rubular.com-ra, és nem felejted el az „m” módosítót a jobb oldali mezőből, akkor a match results alatt ki fogja írni a matched groups-ban a tartalmakat. Ha ezt a két groups-ot összefűzöd egy fájlba, akkor levélfejléc nélküli érvényes HTML-t kapsz.
- Átalakításokhoz parancssorban a grep helyett érdemesebb valami erősebbet használni. Az awk-val és a sed-del az a baj, hogy a többsoros elemzéshez át kell alakítani a mintát, ezért én perl-t szoktam használni. Egy parancs, ami a 01.html-ből kiveszi a levélfejlécet és 01-fejlecnelkul.html-t készít belőle:
perl -0 -pi -e 's/(^.+?<body>).+?(<div class="moz-text-html".+$)/$1\n$2/sgm' <01.html >01-fejlecnelkul.html
Az s/ után találod a regexet, amit kikisérleteztünk, utána jön a két csoport elválasztva egy sortöréssel, és a végén van még néhány módosító, hogy a teljes fájlt egyszerre nézze.
Megjegyzés: mivel a HTML rettentően megengedő, érvényes lenne a <BODY>, <bOdY>, <body> is (és egyéb nyalánkságok), ezért általában nem szoktunk HTML-t regex-szel feldolgozni, hanem valamilyen DOM értelmezőt használunk. Mivel a leveleket az Import/Export Tools generálja teljesen ugyanolyanra, ezért működik a fenti módon a dolog.