-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathespressioni-regolari.html
449 lines (437 loc) · 61.2 KB
/
espressioni-regolari.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
<!DOCTYPE html>
<meta charset=utf-8>
<title>Espressioni regolari - Immersione in Python 3</title>
<!--[if IE]><script src=j/html5.js></script><![endif]-->
<link rel=stylesheet href=dip3.css>
<style>
body{counter-reset:h1 5}
</style>
<link rel=stylesheet media='only screen and (max-device-width: 480px)' href=mobile.css>
<link rel=stylesheet media=print href=print.css>
<meta name=viewport content='initial-scale=1.0'>
<form action=http://www.google.com/cse><div><input type=hidden name=cx value=014021643941856155761:l5eihuescdw><input type=hidden name=ie value=UTF-8> <input type=search name=q size=25 placeholder="powered by Google™"> <input type=submit name=root value=Search></div></form>
<p>Voi siete qui: <a href=index.html>Inizio</a> <span class=u>‣</span> <a href=indice.html#espressioni-regolari>Immersione in Python 3</a> <span class=u>‣</span>
<p id=level>Livello di difficoltà: <span class=u title=intermedio>♦♦♦♢♢</span>
<h1>Espressioni regolari</h1>
<blockquote class=q>
<p><span class=u>❝</span> Alcune persone, quando affrontano un problema, pensano: “Ho capito, userò le espressioni regolari.” Ora hanno due problemi. <span class=u>❞</span><br>— <a href=http://www.jwz.org/hacks/marginal.html>Jamie Zawinski</a>
</blockquote>
<p id=toc>
<h2 id=divingin>Immersione!</h2>
<p class=f>Generalmente, estrarre una specifica porzione da un testo molto lungo non è un’impresa facile. In Python le stringhe sono dotate di metodi per effettuare ricerche e sostituzioni: <code>index()</code>, <code>find()</code>, <code>split()</code>, <code>count()</code>, <code>replace()</code>, <i class=baa>&</i>c. Ma questi metodi si limitano a gestire i casi più semplici. Per esempio, il metodo <code>index()</code> cerca una singola sottostringa costante, e la ricerca è sempre sensibile alle maiuscole. Per fare ricerche insensibili alle maiuscole di una stringa <var>s</var>, dovete chiamare <code>s.lower()</code> oppure <code>s.upper()</code> e assicurarvi che le vostre stringhe di ricerca contengano i caratteri appropriati per corrispondere. I metodi <code>replace()</code> e <code>split()</code> hanno le stesse limitazioni.
<p>Se il vostro obiettivo può essere raggiunto con i metodi delle stringhe, dovreste usarli. Sono veloci, semplici e facili da leggere, e c’è molto da dire a favore del codice veloce, semplice e leggibile. Ma se state lavorando con le stringhe e utilizzate una grande quantità di funzioni differenti insieme a una serie di istruzioni <code>if</code> per gestire casi particolari, o se state combinando tra loro chiamate a <code>split()</code> e <code>join()</code> per affettare e ricomporre le vostre stringhe, potreste aver bisogno di avvalervi delle espressioni regolari.
<p>Le espressioni regolari sono un modo potente e (per la maggior parte) standard per cercare, sostituire e riconoscere testo tramite complessi pattern di caratteri. Sebbene la sintassi delle espressioni regolari sia ermetica e diversa dal normale codice, il risultato potrebbe rivelarsi <em>più</em> leggibile di una soluzione manuale che usa una lunga catena di metodi delle stringhe. Le espressioni regolari vi permettono anche di inserire commenti al loro interno, in modo che possiate documentare le singole parti che le compongono.
<blockquote class='note compare perl5'>
<p><span class=u>☞</span>Se avete usato le espressioni regolari in altri linguaggi (come Perl, JavaScript, o PHP), la sintassi utilizzata da Python vi sarà molto familiare. Leggete il riepilogo della documentazione ufficiale sul <a href=http://docs.python.org/dev/library/re.html#module-contents>modulo <code>re</code></a> per avere una descrizione delle funzioni disponibili e dei loro argomenti.
</blockquote>
<p class=a>⁂
<h2 id=streetaddresses>Caso di studio: indirizzi delle vie</h2>
<p>Questa serie di esempi si ispira a un problema reale incontrato sul lavoro diversi anni fa, quando ho avuto bisogno di ripulire e uniformare gli indirizzi delle vie estratti da un sistema legacy prima di inserirli in un sistema più nuovo. (Vedete, non mi invento questa roba sul momento; è veramente utile.) Questo esempio mostra come ho inizialmente affrontato il problema.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>s = '100 NORTH MAIN ROAD'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>s.replace('ROAD', 'RD.')</kbd> <span class=u>①</span></a>
<samp class=pp>'100 NORTH MAIN RD.'</samp>
<samp class=p>>>> </samp><kbd class=pp>s = '100 NORTH BROAD ROAD'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>s.replace('ROAD', 'RD.')</kbd> <span class=u>②</span></a>
<samp class=pp>'100 NORTH BRD. RD.'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>s[:-4] + s[-4:].replace('ROAD', 'RD.')</kbd> <span class=u>③</span></a>
<samp class=pp>'100 NORTH BROAD RD.'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>import re</kbd> <span class=u>④</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>re.sub('ROAD$', 'RD.', s)</kbd> <span class=u>⑤</span></a>
<samp class=pp>'100 NORTH BROAD RD.'</samp></pre>
<ol>
<li>Il mio obiettivo è uniformare un indirizzo in modo che <code>'ROAD'</code> sia sempre abbreviato come <code>'RD.'</code>. A prima vista, pensavo fosse una cosa abbastanza semplice da non dover usare altro che il metodo <code>replace()</code> delle stringhe. Dopo tutto, i dati erano già in maiuscolo, così eventuali corrispondenze mancate con caratteri minuscoli non sarebbero state un problema. E la stringa di ricerca, <code>'ROAD'</code>, era una costante. E in questo caso ingannevolmente semplice, <code>s.replace()</code> effettivamente funziona.
<li>La vita, sfortunatamente, è piena di controesempi, e ne ho subito trovato uno. Il problema qui è che <code>'ROAD'</code> compare due volte nell’indirizzo, una volta come parte del nome della via <code>'BROAD'</code> e una volta come parola a sé. Il metodo <code>replace()</code> vede queste due occorrenze e le sostituisce entrambe senza distinzioni; nel frattempo, io vedo i miei indirizzi che vengono distrutti.
<li>Per risolvere il problema degli indirizzi che contengono più di una sottostringa <code>'ROAD'</code>, potreste ricorrere a qualcosa di simile a questo: cercate e sostituite <code>'ROAD'</code> solo negli ultimi quattro caratteri dell’indirizzo (<code>s[-4:]</code>) e lasciate stare il resto della stringa (<code>s[:-4]</code>). Ma, come potete vedere, questa strategia rischia di diventare immediatamente poco pratica. Per esempio, il pattern dipende dalla lunghezza della stringa che state sostituendo. (Se doveste sostituire <code>'STREET'</code> con <code>'ST.'</code>, dovreste usare <code>s[:-6]</code> e <code>s[-6:].replace(...)</code>.) Vorreste tornare indietro dopo sei mesi e correggere questo dettaglio? Io so che non vorrei.
<li>È il momento di avvalersi delle espressioni regolari. In Python, tutte le funzionalità relative alle espressioni regolari sono contenute nel modulo <code>re</code>.
<li>Date un’occhiata al primo parametro: <code>'ROAD$'</code>. Questa è una semplice espressione regolare che corrisponde a <code>'ROAD'</code> solo quando si trova alla fine di una stringa. Il simbolo <code>$</code> significa “fine della stringa”. (C’è un carattere corrispondente, il caret <code>^</code>, che significa “inizio della stringa”.) Usando la funzione <code>re.sub()</code>, cercate l’espressione regolare <code>'ROAD$'</code> nella stringa <var>s</var> e la sostituite con <code>'RD.'</code>. La funzione trova una corrispondenza con <code>ROAD</code> alla fine della stringa <var>s</var>, ma <em>non</em> trova alcuna corrispondenza con la <code>ROAD</code> che è parte della parola <code>BROAD</code>, perché quella è nel mezzo di <var>s</var>.
</ol>
<aside>^ corrisponde all’inizio di una stringa. $ corrisponde alla fine di una stringa.</aside>
<p>Proseguendo il mio racconto sulla ripulitura degli indirizzi, ho presto scoperto che la stategia precedente, cioè trovare una corrispondenza con <code>'ROAD'</code> alla fine dell’indirizzo, non era poi così adeguata, perché non tutti gli indirizzi includono una designazione della via. Alcuni indirizzi terminano semplicemente con il nome della via. Riuscivo a cavarmela per la maggior parte del tempo, ma se il nome della via fosse stato <code>'BROAD'</code>, allora l’espressione regolare avrebbe trovato una corrispondenza di <code>'ROAD'</code> alla fine della stringa come parte della parola <code>'BROAD'</code>, ma questo non era ciò che volevo.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>s = '100 BROAD'</kbd>
<samp class=p>>>> </samp><kbd class=pp>re.sub('ROAD$', 'RD.', s)</kbd>
<samp class=pp>'100 BRD.'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.sub('\\bROAD$', 'RD.', s)</kbd> <span class=u>①</span></a>
<samp class=pp>'100 BROAD'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.sub(r'\bROAD$', 'RD.', s)</kbd> <span class=u>②</span></a>
<samp class=pp>'100 BROAD'</samp>
<samp class=p>>>> </samp><kbd class=pp>s = '100 BROAD ROAD APT. 3'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>re.sub(r'\bROAD$', 'RD.', s)</kbd> <span class=u>③</span></a>
<samp class=pp>'100 BROAD ROAD APT. 3'</samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.sub(r'\bROAD\b', 'RD.', s)</kbd> <span class=u>④</span></a>
<samp class=pp>'100 BROAD RD. APT 3'</samp></pre>
<ol>
<li>Quello che <em>realmente</em> volevo era trovare una corrispondenza con <code>'ROAD'</code> quando era al termine della stringa <em>ed</em> era una parola a sé (non parte di una parola più grande). Le espressioni regolari usano <code>\b</code> per esprimere che “il limite di una parola deve trovarsi proprio qui”. In Python, l’uso di questo pattern è complicato dalla necessità di effettuare l’escape di ogni carattere di backslash <code>'\'</code> contenuto in una stringa. Questa seccatura viene solitamente chiamata la piaga del backslash, ed è una delle ragioni per cui le espressioni regolari sono più semplici da usare in Perl che in Python. Sfortunatamente, Perl mescola le espressioni regolari con altra sintassi, quindi se avete un problema potrebbe essere difficile capire se è un bug nella sintassi o è un bug nella vostra espressione regolare.
<li>Per evitare la piaga del backslash, potete usare quella che viene chiamata <i>stringa raw</i>, aggiungendo alla stringa un prefisso con la lettera <code>r</code>. Questo prefisso indica che non deve essere eseguito alcun escape sul contenuto di quella stringa; <code>'\t'</code> è un carattere di tabulazione, ma <code>r'\t'</code> è effettivamente il carattere di backslash <code>\</code> seguito dalla lettera <code>t</code>. Vi raccomando di usare sempre stringhe raw quando avete a che fare con espressioni regolari, altrimenti le cose diventano troppo confuse troppo velocemente (e le espressioni regolari sono già abbastanza confuse per conto proprio).
<li><em>*sigh*</em> Sfortunatamente, ho subito trovato altri esempi che contraddicevano la mia logica. In questo caso, l’indirizzo conteneva la parola <code>'ROAD'</code> come parola intera, ma non era alla fine, perché l’indirizzo aveva un numero di appartamento dopo il nome della via. Dato che <code>'ROAD'</code> non si trova alla fine della stringa, non c’è alcuna corrispondenza, quindi l’intera chiamata a <code>re.sub()</code> non riesce a sostituire nulla e restituisce come risultato la stringa originale, ma questo non è ciò che desiderate.
<li>Per risolvere questo problema, ho rimosso il carattere <code>$</code> e ho aggiunto un altro <code>\b</code>. Ora l’espressione regolare dice “trova una corrispondenza con <code>'ROAD'</code> quando è una parola intera in qualsiasi punto della stringa”, che sia alla fine, all’inizio, o da qualche parte nel mezzo.
</ol>
<p class=a>⁂
<h2 id=romannumerals>Caso di studio: numeri romani</h2>
<p>Avrete probabilmente già incontrato i numeri romani, anche se non li avete riconosciuti. Potreste averli visti nei copyright di vecchi film e spettacoli televisivi (“Copyright <code>MCMXLVI</code>” invece di “Copyright <code>1946</code>”) oppure nelle epigrafi sui muri di biblioteche o università (“fondata nell’anno <code>MDCCCLXXXVIII</code>” invece di “fondata nel <code>1888</code>”). Potreste averli visti anche negli indici dei libri o nei riferimenti bibliografici. È un sistema di rappresentazione numerica che risale all’antico Impero romano (da cui il nome).
<p>Nei numeri romani, ci sono sette caratteri che sono ripetuti e combinati in vari modi per rappresentare i numeri.
<ul>
<li><code>I = 1</code>
<li><code>V = 5</code>
<li><code>X = 10</code>
<li><code>L = 50</code>
<li><code>C = 100</code>
<li><code>D = 500</code>
<li><code>M = 1000</code>
</ul>
<p>Le seguenti sono alcune regole generali per costruire i numeri romani.
<ul>
<li>Talvolta i caratteri sono additivi. <code>I</code> è <code>1</code>, <code>II</code> è <code>2</code>, e <code>III</code> è <code>3</code>. <code>VI</code> è <code>6</code> (letteralmente, “<code>5</code> e <code>1</code>”), <code>VII</code> è <code>7</code>, e <code>VIII</code> è <code>8</code>.
<li>I caratteri delle potenze di dieci (<code>I</code>, <code>X</code>, <code>C</code> e <code>M</code>) possono essere ripetuti fino a tre volte. A <code>4</code>, dovete sottrarre dal carattere del quintuplo più alto successivo. Non potete rappresentare <code>4</code> come <code>IIII</code>; invece, va rappresentato come <code>IV</code> (“<code>1</code> meno di <code>5</code>”). Il numero <code>40</code> è scritto come <code>XL</code> (“<code>10</code> meno di <code>50</code>”), <code>41</code> come <code>XLI</code>, <code>42</code> come <code>XLII</code>, <code>43</code> come <code>XLIII</code>, e poi <code>44</code> come <code>XLIV</code> (“<code>10</code> meno di <code>50</code>, poi <code>1</code> meno di <code>5</code>”).
<li>Talvolta i caratteri sono… l’opposto di additivi. Mettendo certi caratteri prima di altri, sottraete dal valore finale. Per esempio, a <code>9</code>, dovete sottrarre dal carattere della potenza di dieci più alta successiva: <code>8</code> è <code>VIII</code>, ma <code>9</code> è <code>IX</code> (“<code>1</code> meno di <code>10</code>”), non <code>VIIII</code> (dato che il carattere <code>I</code> non può essere ripetuto quattro volte). Il numero <code>90</code> è <code>XC</code>, <code>900</code> è <code>CM</code>.
<li>I caratteri dei quintupli non possono essere ripetuti. Il numero <code>10</code> è sempre rappresentato come <code>X</code>, mai come <code>VV</code>. Il numero <code>100</code> è sempre <code>C</code>, mai <code>LL</code>.
<li>I numeri romani vengono sempre letti da sinistra a destra, così l’ordine dei caratteri ha molta importanza. <code>DC</code> è <code>600</code>; <code>CD</code> è un numero completamente differente (<code>400</code>, “<code>100</code> meno di <code>500</code>”). <code>CI</code> è <code>101</code>; <code>IC</code> non è nemmeno un numero romano valido (perché non potete sottrarre <code>1</code> direttamente da <code>100</code>; dovreste scriverlo come <code>XCIX</code>, cioè “<code>10</code> meno di <code>100</code>, poi <code>1</code> meno di <code>10</code>”).
</ul>
<h3 id=thousands>Controllare le migliaia</h3>
<p>Come potremmo fare per verificare che una stringa arbitraria sia un numero romano valido? Prendiamo una cifra alla volta. Dato che i numeri romani sono sempre scritti dalla cifra più alta a quella più bassa, cominciamo con la più alta: le migliaia. Per i numeri da 1000 in su, le migliaia sono rappresentate da una serie di caratteri <code>M</code>.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import re</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>pattern = '^M?M?M?$'</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'M')</kbd> <span class=u>②</span></a>
<samp><_sre.SRE_Match object at 0106FB58></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MM')</kbd> <span class=u>③</span></a>
<samp><_sre.SRE_Match object at 0106C290></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MMM')</kbd> <span class=u>④</span></a>
<samp><_sre.SRE_Match object at 0106AA38></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MMMM')</kbd> <span class=u>⑤</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, '')</kbd> <span class=u>⑥</span></a>
<samp><_sre.SRE_Match object at 0106F4A8></samp></pre>
<ol>
<li>Questo pattern è composto da tre parti. <code>^</code> fa corrispondere quanto segue solo se è all’inizio della stringa. Se questo non fosse specificato, il pattern troverebbe una corrispondenza a prescindere dalla posizione dei caratteri <code>M</code>, che non è ciò che volete. Voi volete essere sicuri che i caratteri <code>M</code>, se ce ne sono, siano all’inizio della stringa. <code>M?</code> rappresenta una corrispondenza opzionale con un singolo carattere <code>M</code>. Ripetuto tre volte, rappresenta una corrispondenza con una stringa che contiene da zero a tre caratteri <code>M</code> di seguito. E <code>$</code> corrisponde alla fine della stringa. Combinato con il carattere <code>^</code> all’inizio, questo significa che il pattern cerca una corrispondenza con l’intera stringa, senza nessun altro carattere prima o dopo i caratteri <code>M</code>.
<li>L’essenza del modulo <code>re</code> è la funzione <code>search()</code>, che prende un’espressione regolare (<var>pattern</var>) e una stringa (<code>'M'</code>) per provare a trovarvi una corrispondenza con l’espressione regolare. Se viene trovata una corrispondenza, <code>search()</code> restituisce un oggetto che ha vari metodi per descriverla; se non c’è alcuna corrispondenza, <code>search()</code> restituisce <code>None</code>, il valore nullo di Python. Tutto quello che vi interessa al momento è se il pattern trova una corrispondenza, cosa che potete capire guardando solamente al valore restituito da <code>search()</code>. <code>'M'</code> corrisponde a questa espressione regolare, perché la prima <code>M</code> opzionale corrisponde e la seconda e la terza <code>M</code> opzionali vengono ignorate.
<li><code>'MM'</code> corrisponde perché la prima e la seconda <code>M</code> opzionali corrispondono e la terza <code>M</code> viene ignorata.
<li><code>'MMM'</code> corrisponde perché tutti e tre i caratteri <code>M</code> corrispondono.
<li><code>'MMMM'</code> non corrisponde. Tutti e tre i caratteri <code>M</code> corrispondono, poi l’espressione regolare insiste sulla fine della stringa (a causa del carattere <code>$</code>) ma la stringa non è ancora terminata (a causa della quarta <code>M</code>). Quindi <code>search()</code> restituisce <code>None</code>.
<li>È interessante notare che una stringa vuota corrisponde a questa espressione regolare perché tutti i caratteri <code>M</code> sono opzionali.
</ol>
<h3 id=hundreds>Controllare le centinaia</h3>
<aside>? rende un pattern opzionale.</aside>
<p>Le centinaia sono più difficili da trattare rispetto alle migliaia perché ci sono diversi modi mutuamente esclusivi in cui possono essere espresse a seconda del loro valore.
<ul>
<li><code>100 = C</code>
<li><code>200 = CC</code>
<li><code>300 = CCC</code>
<li><code>400 = CD</code>
<li><code>500 = D</code>
<li><code>600 = DC</code>
<li><code>700 = DCC</code>
<li><code>800 = DCCC</code>
<li><code>900 = CM</code>
</ul>
<p>Quindi ci sono quattro possibili pattern:
<ul>
<li><code>CM</code>;
<li><code>CD</code>;
<li>da zero a tre caratteri <code>C</code> (zero se la cifra delle centinaia nel numero intero corrispondente è 0);
<li><code>D</code>, seguito da zero fino a tre caratteri <code>C</code>.
</ul>
<p>Gli ultimi due pattern possono essere combinati nel modo seguente:
<ul>
<li>una <code>D</code> opzionale, seguita da zero fino a tre caratteri <code>C</code>.
</ul>
<p>Questo esempio mostra come validare le centinaia di un numero romano.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import re</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>pattern = '^M?M?M?(CM|CD|D?C?C?C?)$'</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MCM')</kbd> <span class=u>②</span></a>
<samp><_sre.SRE_Match object at 01070390></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MD')</kbd> <span class=u>③</span></a>
<samp><_sre.SRE_Match object at 01073A50></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MMMCCC')</kbd> <span class=u>④</span></a>
<samp><_sre.SRE_Match object at 010748A8></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MCMC')</kbd> <span class=u>⑤</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, '')</kbd> <span class=u>⑥</span></a>
<samp><_sre.SRE_Match object at 01071D98></samp></pre>
<ol>
<li>Questo pattern comincia allo stesso modo del precedente, controllando l’inizio della stringa (<code>^</code>), poi le migliaia (<code>M?M?M?</code>). Dopo prosegue con la nuova parte, tra parentesi, che definisce un insieme di tre pattern mutuamente esclusivi, separati da barre verticali: <code>CM</code>, <code>CD</code> e <code>D?C?C?C?</code> (che contiene una <code>D</code> opzionale seguita da zero fino a tre caratteri <code>C</code> opzionali). Il riconoscitore di espressioni regolari controlla ognuno di questi pattern nell’ordine dato (da sinistra a destra), prende il primo che corrisponde e ignora il resto.
<li><code>'MCM'</code> corrisponde perché la prima <code>M</code> corrisponde, il secondo e il terzo carattere <code>M</code> vengono ignorati e <code>CM</code> corrisponde (quindi i pattern <code>CD</code> e <code>D?C?C?C?</code> non vengono mai considerati). <code>MCM</code> è la rappresentazione di <code>1900</code> come numero romano.
<li><code>'MD'</code> corrisponde perché la prima <code>M</code> corrisponde, il secondo e il terzo carattere <code>M</code> vengono ignorati e il pattern <code>D?C?C?C?</code> corrisponde a <code>D</code> (ognuno dei tre caratteri <code>C</code> opzionali viene ignorato). <code>MD</code> è la rappresentazione di <code>1500</code> come numero romano.
<li><code>'MMMCCC'</code> corrisponde perché tutti e tre i caratteri <code>M</code> corrispondono e il pattern <code>D?C?C?C?</code> corrisponde a <code>CCC</code> (la <code>D</code> è opzionale e viene ignorata). <code>MMMCCC</code> è la rappresentazione di <code>3300</code> come numero romano.
<li><code>'MCMC'</code> non corrisponde. La prima <code>M</code> corrisponde, il secondo e il terzo carattere <code>M</code> vengono ignorati e <code>CM</code> corrisponde, ma poi <code>$</code> non corrisponde perché non siete ancora alla fine della stringa (avete ancora un carattere <code>C</code> senza corrispondenza). Il carattere <code>C</code> non corrisponde come parte del pattern <code>D?C?C?C?</code> perché il pattern mutuamente esclusivo <code>CM</code> ha già trovato una corrispondenza.
<li>È interessante notare che una stringa vuota corrisponde ancora a questo pattern, perché tutti i caratteri <code>M</code> sono opzionali e vengono ignorati e la stringa vuota corrisponde al pattern <code>D?C?C?C?</code> dove tutti i caratteri sono opzionali e vengono ignorati.
</ol>
<p>Whew! Vedete come le espressioni regolari possono diventare brutte velocemente? E avete solo trattato le migliaia e le centinaia dei numeri romani. Se però siete riusciti a seguire tutto questo, le decine e le unità sono facili, perché seguono esattamente lo stesso schema. Ma prima diamo un’occhiata a un altro modo di esprimere il pattern.
<p class=a>⁂
<h2 id=nmsyntax>Usare la sintassi <code>{n,m}</code></h2>
<aside>{1,4} corrisponde da 1 fino a 4 occorrenze di un pattern.</aside>
<p>Nella sezione precedente, avevate a che fare con un pattern dove lo stesso carattere poteva essere ripetuto fino a tre volte. C’è un altro modo, che alcune persone trovano più leggibile, per esprimere questo pattern in un’espressione regolare. Prima di tutto diamo un’occhiata al metodo che abbiamo già usato nell’esempio precedente.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>import re</kbd>
<samp class=p>>>> </samp><kbd class=pp>pattern = '^M?M?M?$'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'M')</kbd> <span class=u>①</span></a>
<samp><_sre.SRE_Match object at 0x008EE090></samp>
<samp class=p>>>> </samp><kbd class=pp>pattern = '^M?M?M?$'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MM')</kbd> <span class=u>②</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp>
<samp class=p>>>> </samp><kbd class=pp>pattern = '^M?M?M?$'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MMM')</kbd> <span class=u>③</span></a>
<samp><_sre.SRE_Match object at 0x008EE090></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MMMM')</kbd> <span class=u>④</span></a>
<samp class=p>>>> </samp></pre>
<ol>
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con la prima <code>M</code> opzionale ma non con la seconda e la terza <code>M</code> (ma questo è OK perché sono opzionali), e poi con la fine della stringa.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con la prima e la seconda <code>M</code> opzionali ma non con la terza <code>M</code> (ma questo è OK perché è opzionale), e poi con la fine della stringa.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con tutte e tre le <code>M</code> opzionali, e poi con la fine della stringa.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con tutte e tre le <code>M</code> opzionali, ma non con la fine della stringa (perché c’è ancora una <code>M</code> senza corrispondenza), quindi il pattern non corrisponde e la funzione restituisce <code>None</code>.
</ol>
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>pattern = '^M{0,3}$'</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'M')</kbd> <span class=u>②</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MM')</kbd> <span class=u>③</span></a>
<samp><_sre.SRE_Match object at 0x008EE090></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MMM')</kbd> <span class=u>④</span></a>
<samp><_sre.SRE_Match object at 0x008EEDA8></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MMMM')</kbd> <span class=u>⑤</span></a>
<samp class=p>>>> </samp></pre>
<ol>
<li>Questo pattern dice: “Trova una corrispondenza con l’inizio della stringa, poi da zero a tre caratteri <code>M</code>, poi la fine della stringa.” Lo 0 e il 3 possono essere qualsiasi numero; se volete trovare una corrispondenza con almeno uno ma non più di tre caratteri <code>M</code>, potete usare <code>M{1,3}</code>.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi una di tre possibili <code>M</code>, poi la fine della stringa.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi due di tre possibili <code>M</code>, poi la fine della stringa.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi tre di tre possibili <code>M</code>, poi la fine della stringa.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi tre di tre possibili <code>M</code>, ma <em>non</em> con la fine della stringa. L’espressione regolare permette solo fino a tre caratteri <code>M</code> prima della fine della stringa, ma ne avete quattro, quindi il pattern non corrisponde e la funzione restituisce <code>None</code>.
</ol>
<h3 id=tensandones>Controllare le decine e le unità</h3>
<p>Ora espandiamo l’espressione regolare dei numeri romani per trattare le decine e le unità. Questo esempio mostra il controllo sulle decine.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>pattern = '^M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)$'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MCMXL')</kbd> <span class=u>①</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MCML')</kbd> <span class=u>②</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MCMLX')</kbd> <span class=u>③</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MCMLXXX')</kbd> <span class=u>④</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MCMLXXXX')</kbd> <span class=u>⑤</span></a>
<samp class=p>>>> </samp></pre>
<ol>
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con la prima <code>M</code> opzionale, poi con <code>CM</code>, poi con <code>XL</code>, poi con la fine della stringa. Ricordate, la sintassi <code>(A|B|C)</code> significa “trova una corrispondenza con esattamente uno solo tra A, B, o C”. Avete trovato una corrispondenza con <code>XL</code>, quindi ignorate le scelte <code>XC</code> e <code>L?X?X?X?</code> e poi vi spostate alla fine della stringa. <code>MCMXL</code> è la rappresentazione di <code>1940</code> come numero romano.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con la prima <code>M</code> opzionale, poi con <code>L?X?X?X?</code>. Di <code>L?X?X?X?</code>, trova una corrispondenza con <code>L</code> e tralascia i tre caratteri <code>X</code> opzionali. Poi vi spostate alla fine della stringa. <code>MCML</code> è la rappresentazione di <code>1950</code> come numero romano.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con la prima <code>M</code> opzionale, poi con <code>CM</code>, poi con la <code>L</code> opzionale e con la prima <code>X</code> opzionale, tralascia la seconda e la terza <code>X</code> opzionali, poi arriva alla fine della stringa. <code>MCMLX</code> è la rappresentazione di <code>1960</code> come numero romano.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con la prima <code>M</code> opzionale, poi con <code>CM</code>, poi con la <code>L</code> opzionale e con tutti e tre i caratteri <code>X</code> opzionali, poi con la fine della stringa. <code>MCMLXXX</code> è la rappresentazione di <code>1980</code> come numero romano.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con la prima <code>M</code> opzionale, poi con <code>CM</code>, poi con la <code>L</code> opzionale e con tutti e tre i caratteri <code>X</code> opzionali, ma poi <em>fallisce la corrispondenza</em> con la fine della stringa perché c’è ancora una <code>X</code> di cui dar conto. Quindi l’intero pattern non corrisponde e la funzione restituisce <code>None</code>. <code>MCMLXXXX</code> non è un numero romano valido.
</ol>
<aside>(A|B) corrisponde al pattern A oppure al pattern B, ma non a entrambi.</aside>
<p>L’espressione per le unità segue lo stesso schema. Vi risparmierò i dettagli e vi mostrerò il risultato finale.
<pre class='nd screen'>
<samp class=p>>>> </samp><kbd class=pp>pattern = '^M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)(IX|IV|V?I?I?I?)$'</kbd>
</pre><p>Come apparirebbe se usassimo la sintassi sostitutiva <code>{n,m}</code>? Questo esempio mostra la nuova sintassi.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MDLV')</kbd> <span class=u>①</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MMDCLXVI')</kbd> <span class=u>②</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MMMDCCCLXXXVIII')</kbd> <span class=u>③</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'I')</kbd> <span class=u>④</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp></pre>
<ol>
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con uno di tre possibili caratteri <code>M</code>, poi con <code>D?C{0,3}</code>. Di questo, trova una corrispondenza con la <code>D</code> opzionale e con zero di tre possibili caratteri <code>C</code>. Proseguendo, trova una corrispondenza con <code>L?X{0,3}</code> tramite la <code>L</code> opzionale e zero di tre possibili caratteri <code>X</code>. Poi trova una corrispondenza con <code>V?I{0,3}</code> tramite la <code>V</code> opzionale e zero di tre possibili caratteri <code>I</code>, e da ultimo con la fine della stringa. <code>MDLV</code> è la rappresentazione di <code>1555</code> come numero romano.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con due di quattro possibili caratteri <code>M</code>, poi con <code>D?C{0,3}</code> tramite una <code>D</code> e uno di tre possibili caratteri <code>C</code>; poi con <code>L?X{0,3}</code> tramite una <code>L</code> e uno di tre possibili caratteri <code>X</code>; poi con <code>V?I{0,3}</code> tramite una <code>V</code> e uno di tre possibili caratteri <code>I</code>; poi con la fine della stringa. <code>MMDCLXVI</code> è la rappresentazione di <code>2666</code> come numero romano.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con tre su tre caratteri <code>M</code>, poi con <code>D?C{0,3}</code> tramite una <code>D</code> e tre su tre caratteri <code>C</code>; poi con <code>L?X{0,3}</code> tramite una <code>L</code> e tre su tre caratteri <code>X</code>; poi con <code>V?I{0,3}</code> tramite una <code>V</code> e tre su tre caratteri <code>I</code>; poi con la fine della stringa. <code>MMMDCCCLXXXVIII</code> è la rappresentazione di <code>3888</code> come numero romano, ed è il numero romano più lungo che potete scrivere senza la sintassi estesa.
<li>Guardate attentamente. (Mi sento come un mago: “Guardate attentamente, bambini. Sto per estrarre un coniglio dal mio cappello.”) Questo trova una corrispondenza con l’inizio della stringa, poi con zero su tre <code>M</code>, poi trova una corrispondenza con <code>D?C{0,3}</code> tralasciando la <code>D</code> opzionale e trovando zero su tre <code>C</code>, poi trova una corrispondenza con <code>L?X{0,3}</code> tralasciando la <code>L</code> opzionale e trovando zero su tre <code>X</code>, poi trova una corrispondenza con <code>V?I{0,3}</code> tralasciando la <code>V</code> opzionale e trovando una su tre <code>I</code>. Poi trova la fine della stringa. Whoa.
</ol>
<p>Se avete seguito tutto questo e lo avete capito alla prima lettura, state andando meglio di me. Ora immaginate di provare a capire l’espressione regolare di qualcun altro in mezzo a una funzione critica di un lungo programma. O immaginate addirittura di tornare a una vostra espressione regolare qualche mese dopo averla scritta. Io l’ho fatto, e non è stata una bella esperienza.
<p>Ora esploriamo una sintassi alternativa che può aiutarvi a mantenere le vostre espressioni regolari.
<p class=a>⁂
<h2 id=verbosere>Espressioni regolari verbose</h2>
<p>Finora avete avuto a che fare solamente con quelle che chiamerò espressioni regolari “compatte”. Come avete visto, sono difficili da leggere, e anche se capite cosa fa una di esse non c’è alcuna garanzia che sarete in grado di capirlo nuovamente sei mesi dopo. Quello di cui avete davvero bisogno è di inserire documentazione in linea.
<p>Python vi permette di farlo grazie a quelle che vengono chiamate <i>espressioni regolari verbose</i>. Un’espressione regolare verbosa è diversa da un’espressione regolare compatta sotto due aspetti.
<ul>
<li>Lo spazio bianco viene ignorato. Spazi, tabulazioni e ritorni a capo non corrispondono a spazi, tabulazioni e ritorni a capo. Non vengono mai considerati. (Se volete utilizzare uno spazio in un’espressione regolare verbosa, dovete effettuarne l’escape mettendogli davanti un backslash.)
<li>I commenti sono ignorati. Un commento in un’espressione regolare verbosa è come un commento nel codice Python: comincia con un carattere <code>#</code> e prosegue fino alla fine della riga. In questo caso è un commento all’interno di una stringa su più righe invece che all’interno del vostro codice sorgente, ma funziona allo stesso modo.
</ul>
<p>Queste differenze diventeranno più chiare con un esempio. Rivediamo l’espressione regolare compatta con la quale stavate lavorando e trasformiamola in un’espressione regolare verbosa. Questo esempio mostra come fare.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>pattern = '''
^ # inizio della stringa
M{0,3} # migliaia - da 0 a 3 M
(CM|CD|D?C{0,3}) # centinaia - 900 (CM), 400 (CD), 0-300 (da 0 a 3 C),
# o 500-800 (D, seguita da 0 fino a 3 C)
(XC|XL|L?X{0,3}) # decine - 90 (XC), 40 (XL), 0-30 (da 0 a 3 X),
# o 50-80 (L, seguita da 0 fino a 3 X)
(IX|IV|V?I{0,3}) # unità - 9 (IX), 4 (IV), 0-3 (da 0 a 3 I),
# o 5-8 (V, seguita da 0 fino a 3 I)
$ # fine della stringa
'''</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'M', re.VERBOSE)</kbd> <span class=u>①</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MCMLXXXIX', re.VERBOSE)</kbd> <span class=u>②</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'MMMDCCCLXXXVIII', re.VERBOSE)</kbd> <span class=u>③</span></a>
<samp><_sre.SRE_Match object at 0x008EEB48></samp>
<a><samp class=p>>>> </samp><kbd class=pp>re.search(pattern, 'M')</kbd> <span class=u>④</span></a></pre>
<ol>
<li>La cosa più importante da ricordare quando usate le espressioni regolari verbose è che avete bisogno di passare un argomento aggiuntivo quando lavorate con esse: <code>re.VERBOSE</code> è una costante definita nel modulo <code>re</code> usata per segnalare che il pattern dovrebbe essere trattato come un’espressione regolare verbosa. Come potete vedere, questo pattern ha un bel po’ di spazi bianchi (che vengono tutti ignorati) e diversi commenti (che vengono tutti ignorati). Una volta che ignorate gli spazi bianchi e i commenti, questa è esattamente la stessa espressione regolare che avete visto nella sezione precedente, ma è molto più leggibile.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con una di tre possibili <code>M</code>, poi con <code>CM</code>, poi con <code>L</code> e con tre di tre possibili <code>X</code>, poi con <code>IX</code>, poi con la fine della stringa.
<li>Questo trova una corrispondenza con l’inizio della stringa, poi con tre di tre possibili <code>M</code>, poi con <code>D</code> e con tre di tre possibili <code>C</code>, poi con <code>L</code> e con tre di tre possibili <code>X</code>, poi con <code>V</code> e con tre di tre possibili <code>I</code>, poi con la fine della stringa.
<li>Questo non trova alcuna corrispondenza. Perché? Perché non ha il flag <code>re.VERBOSE</code>, quindi la funzione <code>re.search()</code> tratta il pattern come un’espressione regolare compatta, dove gli spazi bianchi hanno significato e i simboli di hash sono letterali. Python non è in grado di scoprire da solo se un’espressione regolare è verbosa oppure no. Python presume che ogni espressione regolare sia compatta a meno che non gli diciate esplicitamente che è verbosa.
</ol>
<p class=a>⁂
<h2 id=phonenumbers>Caso di studio: riconoscere i numeri di telefono</h2>
<aside>\d corrisponde a qualsiasi cifra numerica (0–9). \D corrisponde a qualsiasi cosa tranne le cifre.</aside>
<p>Finora vi siete concentrati nel trovare corrispondenze con interi pattern. Un pattern corrisponde, oppure no. Ma le espressioni regolari sono molto più potenti di così. Quando un’espressione regolare <em>trova</em> una corrispondenza, potete recuperarne delle parti specifiche. Potete trovare cosa corrisponde dove.
<p>Questo esempio proviene da un altro problema reale, incontrato ancora una volta in un precedente lavoro. Il problema: riconoscere un numero telefonico americano. Il cliente voleva essere in grado di introdurre il numero senza alcun vincolo (in un singolo campo), ma poi voleva memorizzare separatamente nel database aziendale il codice d’area, il prefisso, il numero e opzionalmente un’estensione. Ho rovistato nel Web e trovato molti esempi di espressioni regolari che dichiaravano di fare proprio questo, ma nessuna era abbastanza permissiva.
<p>Ecco i numeri telefonici che dovevo essere in grado di accettare:
<ul>
<li><code>800-555-1212</code>
<li><code>800 555 1212</code>
<li><code>800.555.1212</code>
<li><code>(800) 555-1212</code>
<li><code>1-800-555-1212</code>
<li><code>800-555-1212-1234</code>
<li><code>800-555-1212x1234</code>
<li><code>800-555-1212 ext. 1234</code>
<li><code>work 1-(800) 555.1212 #1234</code>
</ul>
<p>Una gran varietà! In ognuno di questi casi, avevo bisogno di sapere che il codice d’area era <code>800</code>, il prefisso era <code>555</code> e il resto del numero telefonico era <code>1212</code>. Per quelli con un’estensione, avevo bisogno di sapere che quella estensione era <code>1234</code>.
<p>Lavoriamo per sviluppare una soluzione al riconoscimento dei numeri di telefono. Questo esempio mostra il primo passo.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})$')</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800-555-1212').groups()</kbd> <span class=u>②</span></a>
<samp class=pp>('800', '555', '1212')</samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800-555-1212-1234')</kbd> <span class=u>③</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800-555-1212-1234').groups()</kbd> <span class=u>④</span></a>
<samp class=traceback>Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'groups'</samp></pre>
<ol>
<li>Leggete sempre un’espressione regolare da sinistra a destra. Questa trova una corrispondenza con l’inizio della stringa e poi con <code>(\d{3})</code>. Cos’è <code>\d{3}</code>? Be’, <code>\d</code> significa “qualsiasi cifra numerica” (da 0 fino a <code>9</code>). E <code>{3}</code> significa “trova una corrispondenza con esattamente tre cifre numeriche”; è una variazione della <a href=#nmsyntax>sintassi <code>{n,m}</code></a> che avete visto prima. Mettere il tutto tra parentesi significa “trova una corrispondenza con esattamente tre cifre numeriche <em>e memorizzala come un gruppo che posso richiederti più tardi</em>”. Poi l’espressione regolare trova una corrispondenza con un trattino letterale. Poi trova una corrispondenza con un altro gruppo di esattamente tre cifre. Poi con un altro trattino letterale. Poi con un altro gruppo di esattamente quattro cifre. Poi trova una corrispondenza con la fine della stringa.
<li>Per avere accesso ai gruppi che il motore di espressioni regolari ha memorizzato durante il riconoscimento, usate il metodo <code>groups()</code> sull’oggetto che il metodo <code>search()</code> vi restituisce. Invocare <code>groups()</code> vi restituirà una tupla di tanti gruppi quanti ne avete definiti nell’espressione regolare. In questo caso, avete definito tre gruppi: uno di tre cifre, uno di tre cifre e uno di quattro cifre.
<li>Questa espressione regolare non è la risposta finale, perché non gestisce un numero telefonico con un’estensione alla fine. Per fare questo, avrete bisogno di espandere l’espressione regolare.
<li>E questo è il motivo per cui non dovreste mai “concatenare” i metodi <code>search()</code> e <code>group()</code> nel codice di produzione. Se il metodo <code>search()</code> non restituisce alcuna corrispondenza, restituisce <a href=tipi-di-dato-nativi.html#none><code>None</code></a> invece di un oggetto corrispondenza delle espressioni regolari. Ovviamente, invocare <code>None.groups()</code> solleva un’eccezione: <code>None</code> non possiede un metodo <code>groups()</code>. (Naturalmente, la cosa è un po’ meno ovvia quando vedete sorgere questa eccezione dalle profondità del vostro codice. Sì, parlo per esperienza qui.)
</ol>
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern = re.compile(r'^(\d{3})-(\d{3})-(\d{4})-(\d+)$')</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800-555-1212-1234').groups()</kbd> <span class=u>②</span></a>
<samp class=pp>('800', '555', '1212', '1234')</samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800 555 1212 1234')</kbd> <span class=u>③</span></a>
<samp class=p>>>> </samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800-555-1212')</kbd> <span class=u>④</span></a>
<samp class=p>>>> </samp></pre>
<ol>
<li>Questa espressione regolare è quasi identica alla precedente. Esattamente come prima, trova una corrispondenza con l’inizio della stringa, poi con un gruppo di tre cifre da memorizzare, poi con un trattino, poi con un gruppo di tre cifre da memorizzare, poi con un trattino, poi con un gruppo di quattro cifre da memorizzare. La novità è che poi trova una corrispondenza con un altro trattino, poi con un gruppo di una o più cifre da memorizzare, e poi con la fine della stringa.
<li>Il metodo <code>groups()</code> ora restituisce una tupla di quattro elementi, dato che l’espressione regolare ora definisce quattro gruppi da memorizzare.
<li>Sfortunatamente, anche questa espressione regolare non è la risposta finale, perché presume che le differenti parti di un numero telefonico siano separate da trattini. E se invece fossero separate da spazi, o virgole, o punti? Avete bisogno di una soluzione più generale per trovare una corrispondenza con diversi tipi di separatori.
<li>Oops! Non solo questa espressione regolare non fa tutto quello che volete, ma in realtà è un passo indietro, perché ora non potete più riconoscere numeri telefonici <em>senza</em> un’estensione. Questo non è per niente quello che volevate; se l’estensione c’è volete sapere qual è, ma se non è presente volete comunque sapere quali sono le diverse parti del numero principale.
</ol>
<p>L’esempio seguente mostra l’espressione regolare che gestisce i separatori tra le diverse parti del numero di telefono.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern = re.compile(r'^(\d{3})\D+(\d{3})\D+(\d{4})\D+(\d+)$')</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800 555 1212 1234').groups()</kbd> <span class=u>②</span></a>
<samp class=pp>('800', '555', '1212', '1234')</samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800-555-1212-1234').groups()</kbd> <span class=u>③</span></a>
<samp class=pp>('800', '555', '1212', '1234')</samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('80055512121234')</kbd> <span class=u>④</span></a>
<samp class=p>>>> </samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800-555-1212')</kbd> <span class=u>⑤</span></a>
<samp class=p>>>> </samp></pre>
<ol>
<li>Allacciatevi le cinture. State trovando una corrispondenza con l’inizio della stringa, poi con un gruppo di tre cifre, poi con <code>\D+</code>. Cosa diavolo è quello? Ebbene, <code>\D</code> corrisponde a qualsiasi carattere <em>tranne</em> una cifra numerica, e <code>+</code> significa “1 o più”. Così <code>\D+</code> corrisponde a uno o più caratteri che non sono cifre. Questo è ciò che usate al posto di un trattino letterale per provare a trovare una corrispondenza con separatori differenti.
<li>Usare <code>\D+</code> invece di <code>-</code> significa che ora potete trovare una corrispondenza con numeri le cui parti sono separate da spazi invece che da trattini.
<li>Naturalmente, i numeri telefonici separati da trattini funzionano ancora.
<li>Sfortunatamente, questa non è ancora la risposta finale, perché presume che ci sia sempre un separatore. E se il numero telefonico fosse introdotto senza nessuno spazio o trattino?
<li>Oops! Questo non ha ancora risolto il problema dell’estensione obbligatoria. Ora avete due problemi, ma potete risolverli entrambi con la stessa tecnica.
</ol>
<p>Il prossimo esempio mostra l’espressione regolare per gestire numeri di telefono <em>senza</em> separatori.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern = re.compile(r'^(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('80055512121234').groups()</kbd> <span class=u>②</span></a>
<samp class=pp>('800', '555', '1212', '1234')</samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800.555.1212 x1234').groups()</kbd> <span class=u>③</span></a>
<samp class=pp>('800', '555', '1212', '1234')</samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800-555-1212').groups()</kbd> <span class=u>④</span></a>
<samp class=pp>('800', '555', '1212', '')</samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('(800)5551212 x1234')</kbd> <span class=u>⑤</span></a>
<samp class=p>>>> </samp></pre>
<ol>
<li>L’unico cambiamento che avete fatto rispetto all’ultimo passo è sostituire tutti i <code>+</code> con <code>*</code>. Invece di usare <code>\D+</code> tra le parti del numero telefonico, ora trovate una corrispondenza con <code>\D*</code>. Ricordate che <code>+</code> significa “1 o più”? Ebbene, <code>*</code> significa “zero o più”. Così ora dovreste essere in grado di riconoscere i numeri telefonici anche quando non c’è alcun carattere di separazione.
<li>E guardate un po’, effettivamente funziona. Perché? Trovate una corrispondenza con l’inizio della stringa, poi con un gruppo di tre cifre da memorizzare (<code>800</code>), poi con zero caratteri non numerici, poi con un gruppo di tre cifre da memorizzare (<code>555</code>), poi con zero caratteri non numerici, poi con un gruppo di quattro cifre da memorizzare (<code>1212</code>), poi con zero caratteri non numerici, poi con un gruppo di un numero arbitrario di cifre da memorizzare (<code>1234</code>), poi con la fine della stringa.
<li>Anche altre variazioni funzionano, adesso: punti invece di trattini, e sia uno spazio che una <code>x</code> prima dell’estensione.
<li>Infine, avete risolto l’altro annoso problema: le estensioni sono di nuovo opzionali. Se nessuna estensione viene trovata, il metodo <code>group()</code> restituisce ancora una tupla di quattro elementi, ma il quarto elemento è solamente una stringa vuota.
<li>Odio portare cattive notizie, ma non avete ancora finito. Qual è il problema qui? C’è un carattere in più prima del codice d’area, ma l’espressione regolare presume che il codice d’area sia la prima cosa all’inizio della stringa. Nessun problema, potete usare la stessa tecnica degli “zero o più caratteri non numerici” per saltare i caratteri iniziali prima del codice d’area.
</ol>
<p>L’esempio seguente mostra come gestire i caratteri iniziali nei numeri di telefono.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern = re.compile(r'^\D*(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('(800)5551212 ext. 1234').groups()</kbd> <span class=u>②</span></a>
<samp class=pp>('800', '555', '1212', '1234')</samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800-555-1212').groups()</kbd> <span class=u>③</span></a>
<samp class=pp>('800', '555', '1212', '')</samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('work 1-(800) 555.1212 #1234')</kbd> <span class=u>④</span></a>
<samp class=p>>>> </samp></pre>
<ol>
<li>Questo è lo stesso pattern dell’esempio precedente, a parte il fatto che ora state trovando una corrispondenza con <code>\D*</code> (zero o più caratteri non numerici) prima del primo gruppo da memorizzare (il codice d’area). Notate che non state memorizzando questi caratteri non numerici (non sono tra parentesi). Se li trovate, semplicemente li saltate e poi cominciate a memorizzare il codice d’area in qualsiasi punto si trovi.
<li>Potete riconoscere con successo anche i numeri telefonici con una parentesi aperta prima del codice d’area. (La parentesi chiusa dopo il codice d’area è già gestita: viene trattata come un separatore non numerico e corrisponde al <code>\D*</code> dopo il primo gruppo da memorizzare.)
<li>Giusto come prova del nove per assicurarvi di non aver guastato nulla che prima funzionava. Dato che i caratteri iniziali sono interamente opzionali, questo trova una corrispondenza con l’inizio della stringa, poi con zero caratteri non numerici, poi con un gruppo di tre cifre da memorizzare (<code>800</code>), poi con un carattere non numerico (il trattino), poi con un gruppo di tre cifre da memorizzare (<code>555</code>), poi con un carattere non numerico (il trattino), poi con un gruppo di quattro cifre da memorizzare (<code>1212</code>), poi con zero caratteri non numerici, poi con un gruppo di zero cifre da memorizzare, poi con la fine della stringa.
<li>Questa è una delle situazioni in cui le espressioni regolari mi fanno venire voglia di cavarmi gli occhi con un oggetto spuntato. Perché questo numero di telefono non corrisponde? Perché c’è un <code>1</code> prima del codice d’area, ma avete supposto che tutti i caratteri prima del codice d’area fossero caratteri non numerici (<code>\D*</code>). Aargh.
</ol>
<p>Rivediamo il nostro lavoro per un secondo. Finora le espressioni regolari hanno tutte trovato una corrispondenza dall’inizio della stringa. Ma ora scoprite che all’inizio della stringa potrebbe esserci una quantità indeterminata di caratteri che volete ignorare. Piuttosto che provare a trovare una corrispondenza con tutta quella roba solo per scartarla, adottiamo un approccio differente: evitiamo di cercare esplicitamente una corrispondenza con l’inizio della stringa. Questo approccio è mostrato nel prossimo esempio.
<pre class=screen>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern = re.compile(r'(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')</kbd> <span class=u>①</span></a>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('work 1-(800) 555.1212 #1234').groups()</kbd> <span class=u>②</span></a>
<samp class=pp>('800', '555', '1212', '1234')</samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800-555-1212').groups()</kbd> <span class=u>③</span></a>
<samp class=pp>('800', '555', '1212', '')</samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('80055512121234').groups()</kbd> <span class=u>④</span></a>
<samp class=pp>('800', '555', '1212', '1234')</samp></pre>
<ol>
<li>Notate la mancanza di <code>^</code> in questa espressione regolare. Non state più trovando una corrispondenza con l’inizio della stringa. Non c’è niente che dica che avete bisogno di trovare una corrispondenza tra l’intera stringa in ingresso e la vostra espressione regolare. Il riconoscitore di espressioni regolari si sobbarcherà il duro lavoro necessario a capire quando la stringa in ingresso comincia a corrispondere e proseguirà da lì.
<li>Ora potete riconoscere con successo un numero telefonico che include caratteri iniziali e una cifra iniziale, più un qualsaisi numero di qualsiasi tipo di separatori attorno a ogni parte del numero telefonico.
<li>La prova del nove. Funziona ancora.
<li>Anche questo funziona ancora.
</ol>
<p>Avete visto quanto velocemente un’espressione regolare può finire fuori controllo? Date una rapida occhiata a una qualsiasi delle iterazioni precedenti. Siete in grado di trovare la differenza tra quella e la successiva?
<p>Mentre avete ancora ben chiara la risposta finale (ed è la risposta finale; se avete scoperto un caso che non gestisce, non voglio saperlo) scriviamola come un’espressione regolare verbosa, prima di dimenticare perché avete fatto le scelte che avete fatto.
<pre class=screen>
<samp class=p>>>> </samp><kbd class=pp>phonePattern = re.compile(r"""
# non partire dall'inizio della stringa, il numero può essere ovunque
(\d{3}) # il codice d'area è di 3 cifre (e.g. '800')
\D* # separatore opzionale composto da caratteri che non sono cifre
(\d{3}) # il prefisso è di 3 cifre (e.g. '555')
\D* # separatore opzionale
(\d{4}) # il resto del numero è di 4 cifre (e.g. '1212')
\D* # separatore opzionale
(\d*) # l'estensione opzionale è di un qualsiasi numero di cifre
$ # fine della stringa
""", re.VERBOSE)</kbd>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('work 1-(800) 555.1212 #1234').groups()</kbd> <span class=u>①</span></a>
<samp class=pp>('800', '555', '1212', '1234')</samp>
<a><samp class=p>>>> </samp><kbd class=pp>phonePattern.search('800-555-1212')</kbd> <span class=u>②</span></a>
<samp class=pp>('800', '555', '1212', '')</samp></pre>
<ol>
<li>A parte il fatto di essere distribuita su più righe, questa è esattamente l’espressione regolare dell’ultimo passo, quindi non ci sorprende che riconosca gli stessi ingressi.
<li>La prova del nove conclusiva. Sì, funziona ancora. Avete finito.
</ol>
<p class=a>⁂
<h2 id=summary>Riepilogo</h2>
<p>Questa è solo la più piccola punta dell’iceberg di quello che le espressioni regolari possono fare. In altre parole, anche se ora ne siete completamente sommersi, credetemi, non avete ancora visto niente.
<p>Ora dovreste avere familiarità con le seguenti tecniche:
<ul>
<li><code>^</code> corrisponde all’inizio di una stringa;
<li><code>$</code> corrisponde alla fine di una stringa;
<li><code>\b</code> corrisponde al limite di una parola;
<li><code>\d</code> corrisponde a qualsiasi cifra numerica;
<li><code>\D</code> corrisponde a qualsiasi carattere non numerico;
<li><code>x?</code> corrispondere a un carattere <code>x</code> opzionale (in altre parole, corrisponde a una <code>x</code> zero o una volta);
<li><code>x*</code> corrisponde a <code>x</code> zero o più volte;
<li><code>x+</code> corrisponde a <code>x</code> una o più volte;
<li><code>x{n,m}</code> corrisponde a un carattere <code>x</code> almeno <code>n</code> volte, ma non più di <code>m</code> volte;
<li><code>(a|b|c)</code> corrisponde esattamente a uno tra <code>a</code>, <code>b</code>, o <code>c</code>;
<li><code>(x)</code> in generale è un <em>gruppo di memorizzazione</em>. Potete ottenere il valore di ciò che vi corrisponde usando il metodo <code>groups()</code> dell’oggetto restituito da <code>re.search()</code>.
</ul>
<p>Le espressioni regolari sono estremamente potenti, ma non sono la soluzione corretta per tutti i problemi. Dovreste imparare a conoscerle abbastanza da sapere quando sono appropriate, quando risolveranno i vostri problemi, e quando causeranno più problemi di quelli che risolvono.
<p class=v><a href=stringhe.html rel=prev title='indietro a “Stringhe”'><span class=u>☜</span></a> <a href=generatori.html rel=next title='avanti a “Chiusure & generatori”'><span class=u>☞</span></a>
<p class=c>© 2001–10 <a href=informazioni-sul-libro.html>Mark Pilgrim</a><br>
© 2009–10 <a href=informazioni-sulla-traduzione.html>Giulio Piancastelli</a> per la traduzione italiana
<script src=j/jquery.js></script>
<script src=j/prettify.js></script>
<script src=j/dip3.js></script>