La bellezza delle espressioni regolari (aka: correggendo bgcolor in Wikipedia)

Non so se l’avessi già detto ma amo perdere una consistente quantità di tempo su Wikipedia in lingua italiana. Appena posso lo faccio. È rilassante e stimolante. Ritaglio del tempo soprattutto per svolgere i task più inutili che mi auto-assegno con gioia per sentirmi in pace con me stesso nello sfuggire agli impegni più seri.

Oggi mi sono imbattuto ad esempio in una voce Wikipedia che aveva una semplice tabella con uno sfondo colorato. Colorata con bgcolor.

Non è che tutti dovrebbero sapere che l’HTML si è evoluto giusto un briciolo dalla sua nascita e che roba come bgcolor non si dovrebbe più usare almeno da HTML 4 ovvero almeno dal 1999. Diciamoci la verità, l’ho sempre sospettato, ma manco io avevo la certezza che fosse un attributo deprecato prima di scoprire che la voce [[HTML 4]] non aveva fonti e prima di giungere quindi alle specifiche W3C per pura serendipità. asd

In sostanza, facendo una rapida ricerca, circa un migliaio e mezzo di articoli di Wikipedia ad oggi usavano ancora bgcolor.

Ora, è chiaro che se quasi 1500 voci di Wikipedia hanno una tabella colorata con bgcolor e se ogni browser del pianeta (tranne Internet Exploder) mostra correttamente questo colore, significa che non c’è proprio nessuna fretta a correggerlo. Certo però che un giorno questa migrazione sarà da fare. Un giorno.

Ora sapete il perché ciò mi abbia allettato.

In teoria una sostituzione del genere appare piuttosto semplice. In fondo bisogna solo trovare nel testo della pagina tutto ciò che assomigli a questo:

<td bgcolor="#ff0000"></td>

E trasformarlo in questo:

<td style="background:#ff0000"></td>

Ma i casi da tenere in considerazione sono almeno questi:

<td bgcolor="#ff0000"></td>
<td bgcolor="#f00"></td>
<td bgcolor="ff0000"></td>
<td bgcolor=ff0000></td>
<td bgcolor="f00"></td>
<td bgcolor  = "red"></td>

Senda dimenticare che una becera sostituzione potrebbe provocare attributi style doppi ove ve ne fosse già uno:

<td bgcolor="red"     colspan="2" style="color:blue;">
↓
<td style="color:red" colspan="2" style="color:blue;">

Tagliando corto dopo un’oretta buona ecco qui una casseruola di espressioni regolari che lanciate in sequenza effettuano la corretta conversione di parecchi di questi casi:

1. Una prima sostituzione di tutti i vari colori esadecimali scritti ahimè senza cancelletto davanti tipo bgcolor="ff0000" e che il bot non deve confondere con bgcolor="red" o altri nomi propri che non vogliono il cancelletto davanti:

# Ricerca:
/bgcolor *= *("?)([a-fA-F0-9]{6}|[a-fA-F0-9]{3})+\1/
 
# Sostituzione:
style="background:#$2"

Notare l’utilità dell’operatore \1 nella ricerca per riferirsi al primo apice che può esserci o meno.

2. Segue una sostituzione di tutti i vari colori esadecimali con cancelletto davanti e quelli dal nome specifico:

# Ricerca
/bgcolor *= *("?)(#[a-z0-9A-Z]{6}|#[a-zA-Z0-9]{3}|[a-zA-Z]{3,})+\1/
 
# Sostituzione
style="background:$2"

3. Per sfizio a questo punto uno potrebbe anche decidere di accorciare tutti i colori dal formato #aabbcc a #abc:

# Ricerca
/#([a-fA-F0-9])\1([a-fA-F0-9])\2([a-fA-F0-9])\3"/
 
# Sostituzione
#$1$2$3"

Notare sempre l’uso dell’operatore \1, \2, \3 per dire che un gruppo sarà ripetuto un’altra volta subito dopo.

4. A questo punto ci si potrebbe ritrovare con un tag HTML con due style come in questa situazione, e che dovranno essere uniti in qualche modo:

<td bgcolor="red"                colspan="2" style="color:blue;"></td>
↓
<td style="background-color:red" colspan="2" style="color:blue;"></td>

E quindi si lancia un’altra espressione regolare per unire tutti gli style presenti nello stesso tag (questa è tenera asd):

# Ricerca
/style="([a-zA-Z0-9-; #:%]+?);?"((?: +[a-zA-Z]+ *= *("?)[a-zA-Z0-9-; #:%]+\3)*) style="([a-zA-Z0-9-; #:%]+?);?"/
 
# Sostituzione
style="$1; $4"$2

Notare il doppio-gruppo innestato (?:()*) in mezzo ai due style. Qui il gruppo centrale è ripetuto da zero ad infinite volte e non deve avere nome (altrimenti si entra in altre dimensioni… asd) mentre il gruppo esterno colleziona queste ripetizioni interne e le salva in $2. Quindi se si ha la situazione:

style="uno" roba=asd pippo="gianni" style="due"

Questo sarà il valore della cattura in $2:

roba=asd pippo="gianni"

E quindi avverrà questo magico accorpamento dei due style, mantenendo il resto del ciarpame:

<td style="background-color:blue;" colspan="2" rowspan=1 style="color:red">
↓
<td style="background-color:blue; color:red" colspan="2" rowspan=1>

Notare che ci si è presi lo scrupolo di non far venire ;; nel concatenare le due proprietà CSS o di dimenticarsi proprio ; nella loro congiunzione.

D’altra parte se fin’ora abbiamo acchiappato e convertito i bgcolor ora questo processo dovrà essere ripetuto per color, valign, align ed altre cose, più o meno in quest’ordine. Perché in quest’ordine? Perché il color va sostituito dopo aver sostituito eventuali bgcolor, perché bgcolor può essere frainteso come color e se non avete voglia di piazzare un lookahead per decidere quando inizi il nome di un attributo HTML… insomma, bisogna considerare l’ordine.

C’è anche da dire che finché si sostituisce solo un singolo attributo come bgcolor si rischia al massimo di creare due style e questo problema è risolto con la sostituzione appena citata. Al contrario se gli attributi iniziano ad essere n occorrerà lanciare n volte l’espressione numero 4 affinché passo dopo passo tutti gli style si vadano ad inglobare passaggio dopo passaggio:

<td bgcolor=red color=red valign=left colspan="1" style="padding:1px">
↓
<td style="background:red" style="color:red" style="vertical-align:left" colspan="1" style="padding:1px">
↓
<td style="background:red; color:red; vertical-align:left" colspan="1" style="padding:1px">
↓
<td style="background:red; color:red; vertical-align:left; padding:1px" colspan="1">

(Come fai ad essere già qui? asd. Torna su a constatare i passaggi finché non ti convinci dell’efficacia della espressione regolare numero 4 e del fatto che basti lanciarla n volte per avere un corretto accorpamento dei n attributi deprecati esplosi nei vari style)

Ebbene, lanciando tutte queste espressi regolari su una pagina a caso ecco il risultato: test.

A questo punto si lancia il mio bottino personalissimo e si corregge Wikipedia:

./replace.php \
--always \
--wiki=itwiki \
--generator=search \
--gsrsearch='insource:bgcolor' \
--regex \
--summary="Bot: cose" \
REGEX1 SOSTITUZIONE1 \
REGEX2 SOSTITUZIONE2 \
...

E per oggi le nostre inutilità le abbiamo fatte.

E anche oggi siamo riusciti a non usare un parser HTML per fare ciò che bisognerebbe fare con un parser HTML. asd

P.S.
Naturalmente non solo è stupido pensare che qualcuno mi ringrazierà per aver fatto una cosa del genere (o forse a farlo sarà il World Wide Web Consortium? chissà. asd) ma sarebbe altrettanto stupido pensare che non sia scappato un errore da qualche parte in qualche corner-case allucinante in qualche voce a bassissima visibilità fuori dai controlli a campione, magari proprio mentre mi ero distratto un momento a rispondere alle domande impertinenti di quel bischero di Ferdi2005, mentre con una pupilla monitoravo il bot e con l’altra gli mandavo una gif di un Sofficino. Quindi, fra 2 anni, un utente verrà giustamente a cazziarmi e dovrò giustamente spiegargli come mai io mi sia permesso di non verificare come mai nella sua specifica circostanza un testo abbia potuto perdere la formattazione desiderata. Dannato Ferdi2005. Dannati Sofficini. asd

La vita non da gioie. Le espressioni regolari neanche. Ma almeno abbiamo dimostrato ancora una volta quanto le espressioni regolari siano bellissime. asd