-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy path11-regex.tex
executable file
·1141 lines (977 loc) · 47 KB
/
11-regex.tex
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
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
% The contents of this file is
% Copyright (c) 2009- Charles R. Severance, All Righs Reserved
\chapter{Expressões regulares}
%\chapter{Regular expressions}
Até agora, temos percorrido os arquivos procurando por padrões e extraindo
pedaços de linhas que achamos interessantes. Temos usado métodos para
manipulação de string como {\tt split} e {\tt find} e usamos listas e
fatiamento de strings para extrair partes das linhas.
%So far we have been reading through files, looking for patterns and extracting various
%bits of lines that we find interesting. We have been using string methods like {\tt split}
%and {\tt find} and using lists and string slicing to extract portions of the lines.
\index{expressões regulares}
\index{regex}
\index{módulo re}
%\index{regular expressions}
%\index{regex}
%\index{re module}
Esta tarefa de busca e extração é tão comum que Python tem uma biblioteca
muito poderosa chamada {\bf expressões regulares} que lida com muitas destas
tarefas de forma muito elegante. O motivo de não introduzirmos expressões
regulares antes no livro é que apesar de elas serem muito poderosas, também
são um pouco complicadas e leva algum tempo para se acostumar com sua sintaxe.
%This task of searching and extracting is so common that Python has a very powerful library
%called {\bf regular expressions} that handles many of these tasks quite elegantly. The
%reason we have not introduced regular expressions earlier in the book is because while they
%are very powerful, they are a little complicated and their syntax takes some getting used to.
Expressões regulares são consideradas quase uma linguagem própria de
programação para pesquisa e análise de strings. Na verdade, livros inteiros
foram escritos sobre o tema expressões regulares. Neste capítulo, cobriremos
apenas noções básicas do assunto. Para mais detalhes sobre regulares
expressões, veja:
%Regular expressions are almost their own little programming language for searching and parsing
%strings. As a matter of fact, entire books have been written on the topic of regular expressions.
%In this chapter, we will only cover the basics of regular expressions. For more detail on regular
%expressions, see:
\url{http://en.wikipedia.org/wiki/Regular_expression}
\url{https://docs.python.org/2/library/re.html}
A biblioteca de expressão regular {\tt re} deve ser importada para o seu
programa antes que você possa usá-la. O uso mais simples da biblioteca de
expressão regular é a função {\tt search()}. O programa a seguir demonstra
um uso trivial da função {\it search}.
%The regular expression library {\tt re} must be imported into your program before you can use it.
%The simplest use of the regular expression library is the {\tt search()} function. The following
%program demonstrates a trivial use of the search function.
\index{regex!search}
\beforeverb
\begin{verbatim}
import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
if re.search('From:', line) :
print line
\end{verbatim}
\afterverb
%
Abrimos o arquivo, iteramos linha por linha, e usamos a função {\tt search()}
para imprimir apenas as linhas que contém a string ``From:''. Este programa
não usa o real poder das expressões regulares, uma vez que poderíamos
simplesmente usar {\tt line.find()} para obter o mesmo resultado.
%We open the file, loop through each line, and use the regular expression {\tt search()} to
%only print out lines that contain the string ``From:''. This program does not use the real
%power of regular expressions, since we could have just as easily used {\tt line.find()} to
%accomplish the same result.
\index{string!find}
%\index{string!find}
O poder das expressões regulares surge quando adicionamos caracteres especiais
à string de busca, isso nos permite controlar com mais precisão quais as
linhas que casam com a string. Adicionar estes caracteres especiais em nossa
expressão regular nos permite um casamento e extração sofisticada com pouco
código.
%The power of the regular expressions comes when we add special characters to the search string
%that allow us to more precisely control which lines match the string. Adding these special
%characters to our regular expression allow us to do sophisticated matching and extraction while
%writing very little code.
Por exemplo, o acento circunflexo é usado em expressões regulares para
identificar ``o início'' de uma linha. Nós poderíamos mudar nosso programa
para casar apenas linhas em que ``From:'' estivesse no início como a seguir:
%For example, the caret character is used in regular
%expressions to match ``the beginning'' of a line.
%We could change our program to only match
%lines where ``From:'' was at the beginning of the line as follows:
\beforeverb
\begin{verbatim}
import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
if re.search('^From:', line) :
print line
\end{verbatim}
\afterverb
%
Agora vamos casar apenas as linhas que {\em começam com} a string ``From:''.
Esse é um exemplo simples no qual poderíamos ter feito equivalente com o
método {\tt startswith()} da biblioteca string. Mas serve para introduzir
a noção de que expressões regulares contém caracteres de ação especiais que
nos dão mais controle sobre o que irá casar com a expressão regular.
%Now we will only match lines that {\em start with} the string ``From:''. This is still a very
%simple example that we could have done equivalently with the {\tt startswith()} method from
%the string library. But it serves to introduce the notion that regular expressions contain
%special action characters that give us more control as to what will match the regular expression.
\index{string!startswith}
%\index{string!startswith}
\section{Casamento de caractere em expressões regulares}
%\section{Character matching in regular expressions}
Existe um grande número de caracteres especiais que nos permitem escrever
expressões regulares ainda mais poderosas. O caractere especial mais
utilizado é o ponto, que casa com qualquer caracter.
%There are a number of other special characters that let us build even more powerful regular
%expressions. The most commonly used special character is the period or full stop, which matches
%any character.
\index{curinga}
\index{regex!curinga}
%\index{wild card}
%\index{regex!wild card}
No exemplo a seguir, a expressão regular ``F..m:'' casaria com qualquer uma
das strings ``From:'', ``Fxxm:'', ``F12m:'', ou ``F!@m:'' porque o caractere
ponto casa com qualquer caractere em um expressão regular.
%In the following example, the regular expression ``F..m:'' would match any of the strings
%``From:'', ``Fxxm:'', ``F12m:'', or ``F!@m:'' since the period characters in the regular
%expression match any character.
\beforeverb
\begin{verbatim}
import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
if re.search('^F..m:', line) :
print line
\end{verbatim}
\afterverb
%
Isto é particularmente poderoso quando combinado à habilidade de indicar que
um caractere pode ser repetido algumas vezes utilizando os caracteres ``*''
ou ``+'' em suas expressões regulares. Esses caracteres especiais significam
que ao invés de casar com um único caractere na string de busca, eles casam
com zero-ou-mais caracteres (no caso do asterisco) ou um-ou-mais caracteres
(no caso do sinal de adição).
%This is particularly powerful when combined with the ability to indicate that a character can
%be repeated any number of times using the ``*'' or ``+'' characters in your regular expression.
%These special characters mean that instead of matching a single character in the search string,
%they match zero-or-more characters (in the case of the asterisk) or one-or-more of the characters
%(in the case of the plus sign).
Podemos ainda diminuir as linhas que casam utilizando um caractere {\bf curinga}
repetido no exemplo seguinte:
%We can further narrow down the lines that we match using a repeated {\bf wild card} character in
%the following example:
\beforeverb
\begin{verbatim}
import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
if re.search('^From:.+@', line) :
print line
\end{verbatim}
\afterverb
%
A string de busca ``\verb"^"From:.+@'' casará com sucesso as linhas que
começam com ``From:'', seguidas por um ou mais caracteres (``.+''), seguidas
por uma arroba. Então casará a seguinte linha:
%The search string ``\verb"^"From:.+@'' will successfully match lines that start with ``From:'',
%followed by one or more characters (``.+''), followed by an at-sign. So this will match the
%following line:
\beforeverb
\begin{alltt}
{\bf From:}\underline{ stephen.marquard}{\bf @}uct.ac.za
\end{alltt}
\afterverb
Você pode pensar no curinga ``.+'' como uma expansão para casar todos os
caracteres entre os dois pontos e o arroba.
%You can think of the ``.+'' wildcard as expanding to match all the characters between the
%colon character and the at-sign.
\beforeverb
\begin{alltt}
{\bf From:}\underline{.+}{\bf @}
\end{alltt}
\afterverb
É bom pensar no sinal de adição e no asterisco como ``insistentes''. Por
exemplo, a string a seguir casaria o último arroba na string com o ``.+'',
como mostrado abaixo:
%It is good to think of the plus and asterisk characters as ``pushy''. For example, the following
%string would match the last at-sign in the string as the ``.+'' pushes outwards, as shown below:
\beforeverb
\begin{alltt}
{\bf From:}\underline{ stephen.marquard@uct.ac.za, csev@umich.edu, and cwen}{\bf @}iupui.edu
\end{alltt}
\afterverb
É possível dizer ao asterisco ou ao sinal de adição para não serem tão
``gananciosos'' adicionando outro caracter. Veja a documentação detalhada
para mais informações sobre como desligar o comportamento ganancioso.
%It is possible to tell an asterisk or plus sign not to be so ``greedy'' by adding
%another character. See the detailed documentation for information on turning off the
%greedy behavior.
\index{ganancioso}
%\index{greedy}
\section{Extraindo dados com expressões regulares}
%\section{Extracting data using regular expressions}
Se quisermos extrair dados de uma string em Python podemos usar o método
{\tt findall()} para extrair tudo das substrings que casam com a expressão
regular. Vamos usar o exemplo de querer extrair qualquer coisa que se pareça
com um endereço de email a partir de qualquer linha, independentemente do
formato. Por exemplo, queremos pegar os endereços de email de cada uma das
seguintes linhas:
%If we want to extract data from a string in Python we can use the {\tt findall()} method to extract
%all of the substrings which match a regular expression. Let's use the example of wanting to extract
%anything that looks like an email address from any line regardless of format. For example, we want
%to pull the email addresses from each of the following lines:
\beforeverb
\begin{verbatim}
From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008
Return-Path: <postmaster@collab.sakaiproject.org>
for <source@collab.sakaiproject.org>;
Received: (from apache@localhost)
Author: stephen.marquard@uct.ac.za
\end{verbatim}
\afterverb
%
Não queremos escrever código para cada tipo de linha, dividindo e fatiando
diferentemente cada linha. O programa seguinte usa {\tt findall()} para
encontrar as linhas com endereço de e-mail e extrair um ou mais endereços de
cada uma dessas linhas.
%We don't want to write code for each of the types of lines, splitting and slicing differently for each
%line. This following program uses {\tt findall()} to find the lines with email addresses in them and
%extract one or more addresses from each of those lines.
\index{findall}
\index{regex!findall}
\beforeverb
\begin{verbatim}
import re
s = 'Hello from csev@umich.edu to cwen@iupui.edu about the meeting @2PM'
lst = re.findall('\S+@\S+', s)
print lst
\end{verbatim}
\afterverb
%
O método {\tt findall()} procura a string no segundo argumento e retorna uma
lista de todas as strings que se parecem com um endereço de e-mail. Estamos
usando uma sequência de dois caracteres que casam com um caractere sem espaço
em branco ({\textbackslash}S).
%The {\tt findall()} method searches the string in the second argument and returns a list of
%all of the strings that look like email addresses. We are using a two-character sequence
%that matches a non-whitespace character ({\textbackslash}S).
A saída do programa seria:
%The output of the program would be:
\beforeverb
\begin{verbatim}
['csev@umich.edu', 'cwen@iupui.edu']
\end{verbatim}
\afterverb
%
Traduzindo a expressão regular, estamos procurando por substrings que tenham
ao menos um caractere sem espaço em branco, seguido de um arroba, seguido de
ao menos mais um caractere sem espaço em branco. A instrução
``{\textbackslash}S+'' casa com o máximo de caracteres sem espaço em branco
possíveis.
%Translating the regular expression, we are looking for substrings that have at least one
%non-whitespace character, followed by an at-sign, followed by at least one more non-whitespace
%character. The ``{\textbackslash}S+'' matches as many non-whitespace characters as possible.
A expressão regular casaria duas vezes (csev@umich.edu e cwen@iupui.edu), mas
não casaria com a string ``@2PM'' porque não há caracteres sem espaço em
branco {\em antes} do arroba. Podemos usar essa expressão regular em um
programa para ler todas as linhas de um arquivo e imprimir qualquer coisa que
se pareça com um endereço de email como a seguir:
%The regular expression would match twice (csev@umich.edu and cwen@iupui.edu), but it would not
%match the string ``@2PM'' because there are no non-blank characters {\em before} the at-sign.
%We can use this regular expression in a program to read all the lines in a file and print out
%anything that looks like an email address as follows:
\beforeverb
\begin{verbatim}
import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
x = re.findall('\S+@\S+', line)
if len(x) > 0 :
print x
\end{verbatim}
\afterverb
%
Nós lemos cada linha e então extraímos todas as substrings que casam com
nossa expressão regular. Uma vez que {\tt findall()} retorna uma lista, nós
simplesmente checamos se o número de elementos na lista retornada é maior que
zero para imprimir apenas as linhas onde encontramos ao menos uma substring
que se pareça com um endereço de email.
%We read each line and then extract all the substrings that match our regular expression.
%Since {\tt findall()} returns a list, we simply check if the number of elements in our returned
%list is more than zero to print only lines where we found at least one substring that looks
%like an email address.
Se rodarmos o programa em {\tt findall()} teremos a seguinte saída:
%If we run the program on {\tt findall()}we get the following output:
\beforeverb
\begin{verbatim}
['wagnermr@iupui.edu']
['cwen@iupui.edu']
['<postmaster@collab.sakaiproject.org>']
['<200801032122.m03LMFo4005148@nakamura.uits.iupui.edu>']
['<source@collab.sakaiproject.org>;']
['<source@collab.sakaiproject.org>;']
['<source@collab.sakaiproject.org>;']
['apache@localhost)']
['source@collab.sakaiproject.org;']
\end{verbatim}
\afterverb
%
Alguns de nossos endereços de email tem caracteres incorretos como
``\verb"<"'' ou ``;'' no começo ou no fim. Vamos declarar que estamos
interessados apenas no pedaço da string que começa e termina com uma letra
ou um número.
%Some of our email addresses have incorrect characters like ``\verb"<"'' or ``;'' at the beginning
%or end. Let's declare that we are only interested in the portion of the string that starts and
%ends with a letter or a number.
Para fazer isso, nós usamos outra funcionalidade das expressões regulares.
Colchetes são usados para indicar um conjunto de vários caracteres aceitáveis
que estamos dispostos a considerar. Num certo sentido, o ``{\textbackslash}S''
está pedindo para casar o conjunto de caracteres sem espaço em branco. Agora
seremos um pouco mais explícitos em termos de caracteres que vamos casar.
%To do this, we use another feature of regular expressions. Square brackets are used to indicate a
%set of multiple acceptable characters we are willing to consider matching. In a sense, the
%``{\textbackslash}S'' is asking to match the set of ``non-whitespace characters''. Now we will be
%a little more explicit in terms of the characters we will match.
Aqui está nossa nova expressão regular:
%Here is our new regular expression:
\beforeverb
\begin{verbatim}
[a-zA-Z0-9]\S*@\S*[a-zA-Z]
\end{verbatim}
\afterverb
%
Isso está ficando um pouco complicado e você pode começar a ver por que
expressão regular é uma pequena linguagem em si mesma. Traduzindo esta
expressão regular, estamos procurando por substrings que começam com uma
{\em única} letra minúscula, letra maiúscula, ou número ``[a-zA-Z0-9]'',
seguida por zero ou mais caracteres sem espaço em branco
(``{\textbackslash}S*''), seguida por um arroba, seguida por zero ou mais
caracteres sem espaço em branco (``{\textbackslash}S*''), seguida por uma
letra maiúscula ou minúscula. Note que mudamos de ``+'' para ``*'' para
indicar zero ou mais caracteres sem espaço em branco uma vez que ``[a-zA-Z0-9]''
já é um caractere sem espaço em branco. Lembre-se que o ``*'' ou ``+'' se
aplica ao caractere imediatamente à esquerda do sinal de adição ou do asterisco.
%This is getting a little complicated and you can begin to see why regular expressions are their own
%little language unto themselves. Translating this regular expression, we are looking for substrings
%that start with a {\em single} lowercase letter, uppercase letter, or number ``[a-zA-Z0-9]'', followed
%by zero or more non-blank characters (``{\textbackslash}S*''), followed by an at-sign, followed by zero
%or more non-blank characters (``{\textbackslash}S*''), followed by an uppercase or lowercase letter.
%Note that we switched from ``+'' to ``*'' to indicate zero or more non-blank characters since ``[a-zA-Z0-9]''
%is already one non-blank character. Remember that the ``*'' or ``+'' applies to the single character
%immediately to the left of the plus or asterisk.
\index{regex!character sets(brackets)}
%\index{regex!character sets(brackets)}
Se usarmos essa expressão em nosso programa, nossos dados serão muito mais limpos:
%If we use this expression in our program, our data is much cleaner:
\beforeverb
\begin{verbatim}
import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
x = re.findall('[a-zA-Z0-9]\S*@\S*[a-zA-Z]', line)
if len(x) > 0 :
print x
\end{verbatim}
\afterverb
%
\beforeverb
\begin{verbatim}
...
['wagnermr@iupui.edu']
['cwen@iupui.edu']
['postmaster@collab.sakaiproject.org']
['200801032122.m03LMFo4005148@nakamura.uits.iupui.edu']
['source@collab.sakaiproject.org']
['source@collab.sakaiproject.org']
['source@collab.sakaiproject.org']
['apache@localhost']
\end{verbatim}
\afterverb
%
Observe que na linha ``source@collab.sakaiproject.org'', nossa expressão
regular eliminou duas letras no fim da string (``\verb">";''). Isso ocorre
porque quando nós adicionamos ``[a-zA-Z]'' ao final de nossa expressão
regular, nós estamos demandando que qualquer string que o analisador de
expressão regular encontre precisa terminar com uma letra. Assim quando se
vê ``\verb">"'' depois de ``sakaiproject.org\verb">";'' ele simplesmente para
na última letra que encontrou e casou. (i.e., o ``g'' foi o último que casou).
%Notice that on the ``source@collab.sakaiproject.org'' lines, our regular expression
%eliminated two letters at the end of the string (``\verb">";''). This is because when we
%append ``[a-zA-Z]'' to the end of our regular expression, we are demanding that whatever
%string the regular expression parser finds must end with a letter. So when it sees the
%``\verb">"'' after ``sakaiproject.org\verb">";'' it simply stops at the last ``matching''
%letter it found (i.e., the ``g'' was the last good match).
Note também que a saída do programa é uma lista do Python que tem uma string
como único elemento da lista.
%Also note that the output of the program is a Python list that has a string as the single
%element in the list.
\section{Combinando busca e extração}
%\section{Combining searching and extracting}
Se quisermos encontrar números em linhas que comecem com a string ``X-'' como:
%If we want to find numbers on lines that start with the string ``X-'' such as:
\beforeverb
\begin{verbatim}
X-DSPAM-Confidence: 0.8475
X-DSPAM-Probability: 0.0000
\end{verbatim}
\afterverb
%
Nós não queremos simplesmente os números de ponto flutuante de quaisquer
linhas. Nós queremos apenas extrair números de linhas que tenham a sintaxe
acima.
%we don't just want any floating-point numbers from any lines. We only want to extract
%numbers from lines that have the above syntax.
Nós podemos construir a seguinte expressão regular para selecionar as linhas:
%We can construct the following regular expression to select the lines:
\beforeverb
\begin{verbatim}
^X-.*: [0-9.]+
\end{verbatim}
\afterverb
%
Traduzindo isso, estamos dizendo que queremos linhas que comecem com ``X-'',
seguido de zero ou mais caracteres (``.*''), seguido de dois pontos (``:'') e,
em seguida, um espaço. Depois do espaço, estamos buscando por um ou mais
caracteres que são ou um dígito (0-9) ou um ponto ``[0-9.]+''. Repare que
dentro dos colchetes, o ponto corresponde a um ponto real (i.e., não é um
curinga dentro dos colchetes).
%Translating this, we are saying, we want lines that start with ``X-'', followed by zero or
%more characters (``.*''), followed by a colon (``:'') and then a space. After the space we are
%looking for one or more characters that are either a digit (0-9) or a period ``[0-9.]+''.
%Note that inside the square brackets, the period matches an actual period (i.e., it is not a
%wildcard between the square brackets).
Essa é uma expressão regular muito justa que casará apenas as linhas em que
estamos interessados como a seguir:
%This is a very tight expression that will pretty much match only the lines we are interested
%in as follows:
\beforeverb
\begin{verbatim}
import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
if re.search('^X\S*: [0-9.]+', line) :
print line
\end{verbatim}
\afterverb
%
Quando rodamos o programa, vemos os dados muito bem filtrados exibindo
apenas as linhas que estamos buscando.
%When we run the program, we see the data nicely filtered to show
%only the lines we are looking for.
\beforeverb
\begin{verbatim}
X-DSPAM-Confidence: 0.8475
X-DSPAM-Probability: 0.0000
X-DSPAM-Confidence: 0.6178
X-DSPAM-Probability: 0.0000
\end{verbatim}
\afterverb
%
Mas agora temos que resolver o problema da extração de números. Enquanto isso
seria simples o suficiente usando {\tt split}, nós podemos usar outra
funcionalidade de expressões regulares tanto para buscar quanto para analisar
as linhas ao mesmo tempo.
%But now we have to solve the problem of extracting the numbers. While it would be simple
%enough to use {\tt split}, we can use another feature of regular expressions to both search
%and parse the line at the same time.
\index{string!split}
%\index{string!split}
Parênteses são outro caractere especial em expressões regulares. Quando você
adiciona parênteses a uma expressão regular, eles são ignorados quando
encontram a string. Mas quando você está usando {\tt findall()}, os parênteses
indicam que enquanto você quer que a expressão inteira case, você apenas está
interessado em extrair o pedaço da substring que case com a expressão regular.
%Parentheses are another special character in regular expressions. When you add parentheses
%to a regular expression, they are ignored when matching the string. But when you are using
%{\tt findall()}, parentheses indicate that while you want the whole expression to match,
%you only are interested in extracting a portion of the substring that matches the regular
%expression.
\index{regex!parentheses}
\index{parentheses!regular expression}
%\index{regex!parentheses}
%\index{parentheses!regular expression}
Então, faremos a seguinte alteração em nosso programa:
%So we make the following change to our program:
\beforeverb
\begin{verbatim}
import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
x = re.findall('^X\S*: ([0-9.]+)', line)
if len(x) > 0 :
print x
\end{verbatim}
\afterverb
%
Em vez de chamar {\tt search()}, podemos adicionar parênteses ao redor da
parte da expressão regular que representa o número de ponto flutuante para
indicar que só desejamos que {\tt findall()} nos devolva o pedaço de número
de ponto flutuante da string correspondente.
%Instead of calling {\tt search()}, we add parentheses around the part of the regular expression
%that represents the floating-point number to indicate we only want {\tt findall()} to give us
%back the floating-point number portion of the matching string.
A saída desse programa é a seguinte:
%The output from this program is as follows:
\beforeverb
\begin{verbatim}
['0.8475']
['0.0000']
['0.6178']
['0.0000']
['0.6961']
['0.0000']
..
\end{verbatim}
\afterverb
%
Os números ainda estão em uma lista e precisam ser convertidos de strings
para ponto flutuante, mas temos usado o poder das expressões regulares tanto
para buscar quanto para extrair as informações interessantes que encontramos.
%The numbers are still in a list and need to be converted from strings to floating point, but we
%have used the power of regular expressions to both search and extract the information we found
%interesting.
Como outro exemplo dessa técnica, se você olhar para o arquivo há uma série de
linhas da seguinte forma:
%As another example of this technique, if you look at the file there are a number of lines
%of the form:
\beforeverb
\begin{verbatim}
Details: http://source.sakaiproject.org/viewsvn/?view=rev&rev=39772
\end{verbatim}
\afterverb
%
Se quisermos extrair todos os números de revisão (o número inteiro no fim
destas linhas) utilizando a mesma técnica vista acima, podemos escrever o
programa seguinte:
%If we wanted to extract all of the revision numbers (the integer number at the end of these lines)
%using the same technique as above, we could write the following program:
\beforeverb
\begin{verbatim}
import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
x = re.findall('^Details:.*rev=([0-9]+)', line)
if len(x) > 0:
print x
\end{verbatim}
\afterverb
%
Traduzindo nossa expressão regular, estamos a procura de linhas que comecem
com ``Details:'', seguido por algum número de caracteres (``.*''), seguido
por ``rev='', e então por um ou mais dígitos. Queremos encontrar linhas que
casem com a expressão regular inteira, mas queremos extrair apenas o número
inteiro no fim da linha, então colocamos o ``[0-9]+'' entre parênteses.
%Translating our regular expression, we are looking for lines that start with ``Details:'',
%followed by any number of characters (``.*''), followed by ``rev='', and then by one or
%more digits. We want to find lines that match the entire expression but we only want to
%extract the integer number at the end of the line, so we surround ``[0-9]+'' with parentheses.
Quando rodamos o programa, obtemos a seguinte saída:
%When we run the program, we get the following output:
\beforeverb
\begin{verbatim}
['39772']
['39771']
['39770']
['39769']
...
\end{verbatim}
\afterverb
%
Lembre-se que o ``[0-9]+'' é ``ganancioso'' e tenta criar uma string de
dígitos tão grande quanto possível antes de extrair esses dígitos. Esse
comportamento ``ganancioso'' é por que pegamos todos os cinco dígitos de
cada número. A biblioteca de expressão regular se expande em ambos os
sentidos até encontrar um não-dígito, ou no início ou no fim de uma linha.
%Remember that the ``[0-9]+'' is ``greedy'' and it tries to make as large a string of digits as
%possible before extracting those digits. This ``greedy'' behavior is why we get all five digits
%for each number. The regular expression library expands in both directions until it encounters a
%non-digit, or the beginning or the end of a line.
Agora podemos utilizar expressões regulares para refazer um exercício do
início do livro no qual estávamos interessados na hora do dia em que uma
mensagem foi enviada. Olhamos as linhas da seguinte forma:
%Now we can use regular expressions to redo an exercise from earlier in the book where we were
%interested in the time of day of each mail message. We looked for lines of the form:
\beforeverb
\begin{verbatim}
From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008
\end{verbatim}
\afterverb
%
e queremos extrair a hora do dia para cada linha. Anteriormente fizemos isso
com duas chamadas de {\tt split}. Primeiro a linha foi dividida em palavras
e, depois, tiramos a quinta palavra e dividimos novamente nos dois pontos para
retirar os dois caracteres em que estavamos interessados.
%and wanted to extract the hour of the day for each line. Previously we did this with two calls
%to {\tt split}. First the line was split into words and then we pulled out the fifth word and split
%it again on the colon character to pull out the two characters we were interested in.
% Adicionar uma seção sobre noções de código frágil
% Add a section on the notion of brittle code
Embora tenha funcionado, o código é realmente muito frágil, pois assume que
as linhas serão bem formatadas. Se você acrescentasse uma checagem de erros
suficiente (ou um grande bloco try/except) para garantir que seu programa
nunca falhará quando receber linhas formatadas incorretamente, o programa
aumentaria para 10-15 linhas de código, o que seria muito difícil de ler.
%While this worked, it actually results in pretty brittle code that is assuming the lines are nicely
%formatted. If you were to add enough error checking (or a big try/except block) to insure that your
%program never failed when presented with incorrectly formatted lines, the code would balloon to
%10-15 lines of code that was pretty hard to read.
Podemos fazer isso de um modo muito mais simples com a expressão regular a seguir:
%We can do this in a far simpler way with the following regular expression:
\beforeverb
\begin{verbatim}
^From .* [0-9][0-9]:
\end{verbatim}
\afterverb
%
A tradução dessa expressão regular é que estamos procurando por linhas que
comecem com ``From '' (note o espaço), seguido por algum número de caracteres
(``.*''), seguido por um espaço, seguido por dois dígitos ``[0-9][0-9]'',
seguido por dois pontos. Essa é a definição do tipo de linhas que estamos
procurando.
%The translation of this regular expression is that we are looking for lines that start with ``From ''
%(note the space), followed by any number of characters (``.*''), followed by a space, followed by two
%digits ``[0-9][0-9]'', followed by a colon character. This is the definition of the kinds of lines
%we are looking for.
A fim de retirar somente a hora utilizando {\tt findall()}, colocamos os dois
dígitos entre parênteses como segue:
%In order to pull out only the hour using {\tt findall()}, we add parentheses around the two digits
%as follows:
\beforeverb
\begin{verbatim}
^From .* ([0-9][0-9]):
\end{verbatim}
\afterverb
%
Isso resulta no seguinte programa:
%This results in the following program:
\beforeverb
\begin{verbatim}
import re
hand = open('mbox-short.txt')
for line in hand:
line = line.rstrip()
x = re.findall('^From .* ([0-9][0-9]):', line)
if len(x) > 0 : print x
\end{verbatim}
\afterverb
%
Quando o programa roda, produz a seguinte saída:
%When the program runs, it produces the following output:
\beforeverb
\begin{verbatim}
['09']
['18']
['16']
['15']
...
\end{verbatim}
\afterverb
%
\section{Caractere de escape}
%\section{Escape character}
Uma vez que usamos caracteres especiais em expressões regulares para casar o
começo ou o fim de uma linha ou especificar curingas, precisamos de uma
maneira de indicar que esses caracteres são ``normais'' e queremos casar o
caractere real como um sinal de dólar ou acento circunflexo.
%Since we use special characters in regular expressions to match the beginning or end of
%a line or specify wild cards, we need a way to indicate that these characters are ``normal''
%and we want to match the actual character such as a dollar sign or caret.
Podemos indicar que queremos simplesmente casar um caractere prefixando o
caractere com uma barra invertida. Por exemplo, podemos encontrar quantidades
de dinheiro com a seguinte expressão regular.
%We can indicate that we want to simply match a character by prefixing that character
%with a backslash. For example, we can find money amounts with the following regular
%expression.
\beforeverb
\begin{verbatim}
import re
x = 'We just received $10.00 for cookies.'
y = re.findall('\$[0-9.]+',x)
\end{verbatim}
\afterverb
%
Já que prefixamos o caractere dólar com uma barra invertida, ele realmente
casará com o dólar na string de entrada ao invés de casar com o ``fim da
linha'', e o resto da expressão regular casa um ou mais dígitos ou o
caractere ponto. {\em Note:} Dentro de colchetes, caracteres não são
``especiais''. Então quando dizemos ``[0-9.]'', realmente significa dígitos
ou um ponto. Fora dos colchetes, um ponto é o caractere ``curinga'' para
casar qualquer caractere. Dentro dos colchetes, o ponto é um ponto.
%Since we prefix the dollar sign with a backslash, it actually matches the dollar sign
%in the input string instead of matching the ``end of line'', and the rest of the regular
%expression matches one or more digits or the period character. {\em Note:} Inside
%square brackets, characters are not ``special''. So when we say ``[0-9.]'', it really
%means digits or a period. Outside of square brackets, a period is the ``wild-card''
%character and matches any character. Inside square brackets, the period is a period.
\section{Resumo}
%\section{Summary}
Embora isso tenha sido apenas uma visão superficial de expressões regulares,
conseguimos aprender um pouco sobre a linguagem de expressões regulares. Elas
são strings de busca com caracteres especiais que informam o que você quer ao
sistema de expressão regular, que por sua vez define o ``casamento'' e o que é
extraído das strings que casaram. Aqui temos alguns desses caracteres especiais
e sequências de caracteres:
%While this only scratched the surface of regular expressions, we have learned a bit about
%the language of regular expressions. They are search strings with special characters in them
%that communicate your wishes to the regular expression system as to what defines ``matching''
%and what is extracted from the matched strings. Here are some of those special characters
%and character sequences:
\verb"^" \newline
Corresponde ao início da linha.
%Matches the beginning of the line.
\$ \newline
Corresponde ao final da linha.
%Matches the end of the line.
. \newline
Corresponde a qualquer caractere (um curinga).
%Matches any character (a wildcard).
{\textbackslash}s \newline
Corresponde a um espaço em branco.
%Matches a whitespace character.
{\textbackslash}S \newline
Corresponde a um caractere sem espaço em branco (oposto do {\textbackslash}s).
%Matches a non-whitespace character (opposite of {\textbackslash}s).
* \newline
Aplica-se ao caractere imediatamente anterior e corresponde a zero ou mais
do(s) caractere(s) anterior(es).
%Applies to the immediately preceding character and indicates to match zero or more of the
%preceding character(s).
*? \newline
Aplica-se ao caractere imediatamente anterior e corresponde a zero ou mais
do(s) caractere(s) anterior(es) em ``modo não ganancioso''.
%Applies to the immediately preceding character and indicates to match zero or more of the
%preceding character(s) in ``non-greedy mode''.
+ \newline
Aplica-se ao caractere imediatamente anterior e corresponde a um ou mais
do(s) caractere(s) anterior(es).
%Applies to the immediately preceding character and indicates to match one or more of the
%preceding character(s).
+? \newline
Aplica-se ao caractere imediatamente anterior e corresponde a um ou mais
do(s) caractere(s) anterior(es) em ``modo não ganancioso''.
%Applies to the immediately preceding character and indicates to match one or more of the
%preceding character(s) in ``non-greedy mode''.
[aeiou] \newline
Corresponde a um único caractere contanto que esteja no conjunto especificado.
Nesse exemplo, corresponderia a ``a'', ``e'', ``i'', ``o'', ou ``u'', mas a
nenhum outro caractere.
%Matches a single character as long as that character is in the specified set. In this example,
%it would match ``a'', ``e'', ``i'', ``o'', or ``u'', but no other characters.
[a-z0-9] \newline
Você pode especificar intervalos de caracteres usando o sinal de subtração.
Esse exemplo é um único caractere que deve ser uma letra minúscula ou um dígito.
%You can specify ranges of characters using the minus sign. This example is a single character
%that must be a lowercase letter or a digit.
[\verb"^"A-Za-z] \newline
Quando o primeiro caractere é um acento circunflexo, ele inverte a lógica.
Esse exemplo, corresponde um único caractere que é qualquer coisa {\em exceto}
uma letra maiúscula ou minúscula.
%When the first character in the set notation is a caret, it inverts the logic. This example
%matches a single character that is anything {\em other than} an uppercase or lowercase letter.
( ) \newline
Quando adicionamos parênteses a uma expressão regular, eles são ignorados
para efeito de correspondência, mas permite extrair um subconjunto específico
da string correspondente ao invés de toda string quando usamos {\tt findall()}.
%When parentheses are added to a regular expression, they are ignored for the purpose of matching,
%but allow you to extract a particular subset of the matched string rather than the whole string
%when using {\tt findall()}.
{\textbackslash}b \newline
Corresponde a uma string vazia, mas somente no começo ou no final de uma palavra.
%Matches the empty string, but only at the start or end of a word.
{\textbackslash}B \newline
Corresponde a uma string vazia, mas não no começo ou no final de uma palavra.
%Matches the empty string, but not at the start or end of a word.
{\textbackslash}d \newline
Corresponde a qualquer dígito decimal; equivalente ao conjunto [0-9].
%Matches any decimal digit; equivalent to the set [0-9].
{\textbackslash}D \newline
Corresponde a qualquer caractere não-dígito; equivalente ao conjunto [\verb"^"0-9].
%Matches any non-digit character; equivalent to the set [\verb"^"0-9].
\section{Seção bônus para usuários de Unix}
%\section{Bonus section for Unix users}
O suporte para pesquisar arquivos utilizando expressões regulares está
presente no sistema operacional Unix desde 1960 e está disponível em quase
todas as linguagens de programação de uma forma ou de outra.
%Support for searching files using regular expressions was built into the Unix operating system
%since the 1960s and it is available in nearly all programming languages in one form or another.
\index{grep}
Na realidade, é um programa de linha de comando integrado ao Unix chamado
{\bf grep} (Generalized Regular Expression Parser - Analisador generalizado
de expressões regulares) que faz o mesmo que os exemplos {\tt search()} nesse
capítulo. Então, se você tem um sistema Macintosh ou Linux pode tentar os
seguintes comandos em sua janela de linha de comando.
%As a matter of fact, there is a command-line program built into Unix
%called {\bf grep} (Generalized Regular Expression Parser) that does pretty much
%the same as the {\tt search()} examples in this chapter. So if you have a
%Macintosh or Linux system, you can try the following commands in your command-line window.
\beforeverb
\begin{verbatim}
$ grep '^From:' mbox-short.txt
From: stephen.marquard@uct.ac.za
From: louis@media.berkeley.edu
From: zqian@umich.edu
From: rjlowe@iupui.edu
\end{verbatim}
\afterverb
%
Isso diz ao {\tt grep} para mostrar as linhas que comecem com a string
``From:'' no arquivo {\tt mbox-short.txt}. Se você experimentar um pouco
o comando {\tt grep} e ler sua documentação encontrará algumas pequenas
diferenças entre as expressões regulares suportadas em Python e as
expressões regulares suportadas pelo {\tt grep}. Por exemplo, {\tt grep} não
suporta o caractere sem espaço em branco ``{\textbackslash}S'' então você
precisará usar uma notação um pouco mais complexa ``[\verb"^" ]'', que
simplesmente significa que corresponde a um caractere que é qualquer coisa
a não ser um espaço.
%This tells {\tt grep} to show you lines that start with the string ``From:'' in the file
%{\tt mbox-short.txt}. If you experiment with the {\tt grep} command a bit and read the
%documentation for {\tt grep}, you will find some subtle differences between the regular
%expression support in Python and the regular expression support in {\tt grep}. As an example,
%{\tt grep} does not support the non-blank character ``{\textbackslash}S'' so you will need to
%use the slightly more complex set notation ``[\verb"^" ]'', which simply means match a
%character that is anything other than a space.
\section{Depuração}
%\section{Debugging}
Python possui uma documentação embutida simples e rudimentar que pode ser
bastante útil se você precisar de uma ajuda para se lembrar do nome exato
de um método em particular. Essa documentação pode ser vista em seu
interpretador Python no modo interativo.
%Python has some simple and rudimentary built-in documentation that can be quite helpful if
%you need a quick refresher to trigger your memory about the exact name of a particular method.
%This documentation can be viewed in the Python interpreter in interactive mode.
Você pode acionar um sistema de ajuda interativa usando {\tt help()}.
%You can bring up an interactive help system using {\tt help()}.
\beforeverb
\begin{verbatim}
>>> help()
Welcome to Python 2.6! This is the online help utility.
If this is your first time using Python, you should definitely check out
the tutorial on the Internet at http://docs.python.org/tutorial/.
Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules. To quit this help utility and
return to the interpreter, just type "quit".
To get a list of available modules, keywords, or topics, type "modules",
"keywords", or "topics". Each module also comes with a one-line summary
of what it does; to list the modules whose summaries contain a given word
such as "spam", type "modules spam".
help> modules
\end{verbatim}
\afterverb
%
Se você sabe que módulo deseja usar, pode usar o comando {\tt dir()} para
encontrar os métodos no módulo da seguinte forma:
%If you know what module you want to use, you can use the {\tt dir()} command to find the methods
%in the module as follows:
\beforeverb
\begin{verbatim}