-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Expand file tree
/
Copy path09_regexp.html
More file actions
854 lines (536 loc) · 92.2 KB
/
Copy path09_regexp.html
File metadata and controls
854 lines (536 loc) · 92.2 KB
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
<!doctype html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Expressões Regulares :: JavaScript Eloquente</title>
<link rel=stylesheet href="css/ejs.css"><script>
var page = {"type":"chapter","number":9}</script></head>
<article>
<nav><a href="08_error.html" title="capítulo anterior" aria-label="capítulo anterior">◂</a> <a href="index.html" title="capa" aria-label="capa">●</a> <a href="10_modules.html" title="próximo capítulo" aria-label="próximo capítulo">▸</a> <button class=help title="ajuda" aria-label="ajuda"><strong>?</strong></button>
</nav>
<h1>Expressões Regulares</h1>
<blockquote>
<p><a class="p_ident" id="p-2nwb1iM20T" href="#p-2nwb1iM20T" tabindex="-1" role="presentation"></a>Algumas pessoas, quando confrontadas com um problema, pensam ‘Eu sei, vou usar expressões regulares.’ Agora elas têm dois problemas.</p>
<footer>Jamie Zawinski</footer>
</blockquote>
<blockquote>
<p><a class="p_ident" id="p-OEOnMMyUL4" href="#p-OEOnMMyUL4" tabindex="-1" role="presentation"></a>Quando você corta contra o veio da madeira, muita força é necessária. Quando você programa contra o veio do problema, muito código é necessário.</p>
<footer>Master Yuan-Ma, <cite>The Book of Programming</cite></footer>
</blockquote><figure class="chapter square-framed"><img src="img/chapter_picture_9.jpg" alt="Illustration of a railroad system representing the syntactic structure of regular expressions"></figure>
<p><a class="p_ident" id="p-qBgzVF/WMK" href="#p-qBgzVF/WMK" tabindex="-1" role="presentation"></a>Ferramentas e técnicas de programação sobrevivem e se espalham de maneira caótica e evolutiva. Nem sempre são as melhores ou mais brilhantes que vencem, mas sim aquelas que funcionam bem o suficiente dentro do nicho certo ou que acontecem de estar integradas com outra tecnologia bem-sucedida.</p>
<p><a class="p_ident" id="p-Qm0fiaYMCG" href="#p-Qm0fiaYMCG" tabindex="-1" role="presentation"></a>Neste capítulo, discutirei uma dessas ferramentas, <em>expressões regulares</em>. Expressões regulares são uma maneira de descrever padrãoes em dados de <em>string</em>. Elas formam uma linguagem pequena e separada que faz parte do JavaScript e de muitas outras linguagens e sistemas.</p>
<p><a class="p_ident" id="p-9U8PKS4HjO" href="#p-9U8PKS4HjO" tabindex="-1" role="presentation"></a>Expressões regulares são ao mesmo tempo terrivelmente estranhas e extremamente úteis. Sua sintaxe é críptica e a interface de programação que o JavaScript fornece para elas é desajeitada. Mas são uma ferramenta poderosa para inspecionar e processar <em>strings</em>. Entender adequadamente expressões regulares fará de você um programador mais eficiente.</p>
<h2><a class="h_ident" id="h-HHD74IV7iJ" href="#h-HHD74IV7iJ" tabindex="-1" role="presentation"></a>Criando uma expressão regular</h2>
<p><a class="p_ident" id="p-b3m+4SfOMS" href="#p-b3m+4SfOMS" tabindex="-1" role="presentation"></a>Uma expressão regular é um tipo de objeto. Ela pode ser construída com o construtor <code>RegExp</code> ou escrita como um valor literal envolvendo um padrão em caracteres de barra (<code>/</code>).</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-O1I2rl+HTy" href="#c-O1I2rl+HTy" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">re1</span> = <span class="tok-keyword">new</span> RegExp(<span class="tok-string">"abc"</span>);
<span class="tok-keyword">let</span> <span class="tok-definition">re2</span> = <span class="tok-string2">/abc/</span>;</pre>
<p><a class="p_ident" id="p-+lDOFGsnct" href="#p-+lDOFGsnct" tabindex="-1" role="presentation"></a>Ambos os objetos de expressão regular representam o mesmo padrão: um caractere <em>a</em> seguido de um <em>b</em> seguido de um <em>c</em>.</p>
<p><a class="p_ident" id="p-VSe6Jk81Zy" href="#p-VSe6Jk81Zy" tabindex="-1" role="presentation"></a>Ao usar o construtor <code>RegExp</code>, o padrão é escrito como uma <em>string</em> normal, então as regras usuais se aplicam para barras invertidas.</p>
<p><a class="p_ident" id="p-9vCJsYqOtB" href="#p-9vCJsYqOtB" tabindex="-1" role="presentation"></a>A segunda notação, onde o padrão aparece entre caracteres de barra, trata barras invertidas de forma um pouco diferente. Primeiro, como uma barra encerra o padrão, precisamos colocar uma barra invertida antes de qualquer barra que queiramos que seja <em>parte</em> do padrão. Além disso, barras invertidas que não fazem parte de códigos de caracteres especiais (como <code>\n</code>) serão <em>preservadas</em>, em vez de ignoradas como são em <em>strings</em>, e mudam o significado do padrão. Alguns caracteres, como interrogações e sinais de mais, têm significados especiais em expressões regulares e devem ser precedidos por uma barra invertida se pretendem representar o próprio caractere.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-QdJ95//DRD" href="#c-QdJ95//DRD" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">aPlus</span> = <span class="tok-string2">/A\+/</span>;</pre>
<h2><a class="h_ident" id="h-Dsp3K6238Y" href="#h-Dsp3K6238Y" tabindex="-1" role="presentation"></a>Testando correspondências</h2>
<p><a class="p_ident" id="p-a2JZzOl9hx" href="#p-a2JZzOl9hx" tabindex="-1" role="presentation"></a>Objetos de expressão regular possuem vários métodos. O mais simples é <code>test</code>. Se você passar uma <em>string</em>, ele retornará um booleano dizendo se a <em>string</em> contém uma correspondência do padrão na expressão.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-Szn1CmrIV5" href="#c-Szn1CmrIV5" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string2">/abc/</span>.test(<span class="tok-string">"abcde"</span>));
<span class="tok-comment">// → true</span>
console.log(<span class="tok-string2">/abc/</span>.test(<span class="tok-string">"abxde"</span>));
<span class="tok-comment">// → false</span></pre>
<p><a class="p_ident" id="p-hAG4bAmN51" href="#p-hAG4bAmN51" tabindex="-1" role="presentation"></a>Uma expressão regular consistindo apenas de caracteres não-especiais simplesmente representa aquela sequência de caracteres. Se <em>abc</em> ocorre em qualquer lugar na <em>string</em> contra a qual estamos testando (não apenas no início), <code>test</code> retornará <code>true</code>.</p>
<h2><a class="h_ident" id="h-JdVGOTeHyG" href="#h-JdVGOTeHyG" tabindex="-1" role="presentation"></a>Conjuntos de caracteres</h2>
<p><a class="p_ident" id="p-AJdc/pDiHr" href="#p-AJdc/pDiHr" tabindex="-1" role="presentation"></a>Descobrir se uma <em>string</em> contém <em>abc</em> poderia ser feito igualmente com uma chamada a <code>indexOf</code>. Expressões regulares são úteis porque nos permitem descrever padrãoes mais complicados.</p>
<p><a class="p_ident" id="p-mcCHk32gG+" href="#p-mcCHk32gG+" tabindex="-1" role="presentation"></a>Digamos que queremos encontrar qualquer número. Em uma expressão regular, colocar um conjunto de caracteres entre colchetes faz com que aquela parte da expressão corresponda a qualquer um dos caracteres entre os colchetes.</p>
<p><a class="p_ident" id="p-AymCd5+xC3" href="#p-AymCd5+xC3" tabindex="-1" role="presentation"></a>Ambas as expressões a seguir correspondem a todas as <em>strings</em> que contêm um dígito:</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-Z3UJdL//cY" href="#c-Z3UJdL//cY" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string2">/[0123456789]/</span>.test(<span class="tok-string">"in 1992"</span>));
<span class="tok-comment">// → true</span>
console.log(<span class="tok-string2">/[0-9]/</span>.test(<span class="tok-string">"in 1992"</span>));
<span class="tok-comment">// → true</span></pre>
<p><a class="p_ident" id="p-/GbbBDmuLE" href="#p-/GbbBDmuLE" tabindex="-1" role="presentation"></a>Dentro de colchetes, um hífen (<code>-</code>) entre dois caracteres pode ser usado para indicar um intervalo de caracteres, onde a ordenação é determinada pelo número Unicode do caractere. Os caracteres 0 a 9 ficam um ao lado do outro nessa ordenação (códigos 48 a 57), então <code>[0-9]</code> cobre todos eles e corresponde a qualquer dígito.</p>
<p><a class="p_ident" id="p-yJ5MHqf/4f" href="#p-yJ5MHqf/4f" tabindex="-1" role="presentation"></a>Vários grupos comuns de caracteres têm seus próprios atalhos embutidos. Dígitos são um deles: <code>\d</code> significa a mesma coisa que <code>[0-9]</code>.</p>
<table>
<tr><td><code>\d</code></td><td>Qualquer caractere de dígito</td>
</tr>
<tr><td><code>\w</code></td><td>Um caractere alfanumérico (“caractere de palavra”)</td>
</tr>
<tr><td><code>\s</code></td><td>Qualquer caractere de espaço em branco (espaço, tab, nova linha e similares)</td>
</tr>
<tr><td><code>\D</code></td><td>Um caractere que <em>não</em> é um dígito</td>
</tr>
<tr><td><code>\W</code></td><td>Um caractere não-alfanumérico</td>
</tr>
<tr><td><code>\S</code></td><td>Um caractere que não é espaço em branco</td>
</tr>
<tr><td><code>.</code></td><td>Qualquer caractere exceto nova linha</td>
</tr>
</table>
<p><a class="p_ident" id="p-sxpGg1ReJe" href="#p-sxpGg1ReJe" tabindex="-1" role="presentation"></a>Você poderia corresponder a um formato de data e hora como 01-30-2003 15:20 com a seguinte expressão:</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-Y0e7M8nAL0" href="#c-Y0e7M8nAL0" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">dateTime</span> = <span class="tok-string2">/\d\d-\d\d-\d\d\d\d \d\d:\d\d/</span>;
console.log(dateTime.test(<span class="tok-string">"01-30-2003 15:20"</span>));
<span class="tok-comment">// → true</span>
console.log(dateTime.test(<span class="tok-string">"30-jan-2003 15:20"</span>));
<span class="tok-comment">// → false</span></pre>
<p><a class="p_ident" id="p-iYzy6DbLiS" href="#p-iYzy6DbLiS" tabindex="-1" role="presentation"></a>Essa expressão regular parece completamente horrível, não é? Metade dela são barras invertidas, produzindo um ruído de fundo que dificulta identificar o padrão real expresso. Veremos uma versão um pouco melhorada dessa expressão <a href="09_regexp.html#date_regexp_counted">mais adiante</a>.</p>
<p><a class="p_ident" id="p-xHX08QjQaW" href="#p-xHX08QjQaW" tabindex="-1" role="presentation"></a>Esses códigos de barra invertida também podem ser usados dentro de colchetes. Por exemplo, <code>[\d.]</code> significa qualquer dígito ou um caractere de ponto. O ponto em si, entre colchetes, perde seu significado especial. O mesmo vale para outros caracteres especiais, como o sinal de mais (<code>+</code>).</p>
<p><a class="p_ident" id="p-qrznlTqjZ7" href="#p-qrznlTqjZ7" tabindex="-1" role="presentation"></a>Para <em>inverter</em> um conjunto de caracteres — isto é, expressar que você quer corresponder a qualquer caractere <em>exceto</em> os do conjunto — você pode escrever um caractere circunflexo (<code>^</code>) após o colchete de abertura.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-jj4NG87MXv" href="#c-jj4NG87MXv" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">nonBinary</span> = <span class="tok-string2">/[^01]/</span>;
console.log(nonBinary.test(<span class="tok-string">"1100100010100110"</span>));
<span class="tok-comment">// → false</span>
console.log(nonBinary.test(<span class="tok-string">"0111010112101001"</span>));
<span class="tok-comment">// → true</span></pre>
<h2><a class="h_ident" id="h-OydIuXLrbx" href="#h-OydIuXLrbx" tabindex="-1" role="presentation"></a>Caracteres internacionais</h2>
<p><a class="p_ident" id="p-mE0SZ1YKda" href="#p-mE0SZ1YKda" tabindex="-1" role="presentation"></a>Devido à implementação inicial simplista do JavaScript e ao fato de que essa abordagem simplista foi mais tarde consolidada como comportamento padrão, as expressões regulares do JavaScript são bastante limitadas em relação a caracteres que não aparecem na língua inglesa. Por exemplo, no que diz respeito às expressões regulares do JavaScript, um “caractere de palavra” é apenas um dos 26 caracteres do alfabeto latino (maiúsculo ou minúsculo), dígitos decimais e, por algum motivo, o caractere sublinhado. Coisas como <em>é</em> ou <em>β</em>, que são definitivamente caracteres de palavra, não correspondem a <code>\w</code> (e <em>correspondem</em> a <code>\W</code> maiúsculo, a categoria de não-palavra).</p>
<p><a class="p_ident" id="p-Wj6JAmP68e" href="#p-Wj6JAmP68e" tabindex="-1" role="presentation"></a>Por um estranho acidente histórico, <code>\s</code> (espaço em branco) não tem esse problema e corresponde a todos os caracteres que o padrão Unicode considera espaço em branco, incluindo coisas como o espaço não-quebrável e o separador de vogais mongol.</p>
<p><a class="p_ident" id="p-7LOA5H3J68" href="#p-7LOA5H3J68" tabindex="-1" role="presentation"></a>É possível usar <code>\p</code> em uma expressão regular para corresponder a todos os caracteres aos quais o padrão Unicode atribui uma dada propriedade. Isso nos permite corresponder a coisas como letras de uma maneira mais cosmopolita. Porém, novamente devido à compatibilidade com os padrões originais da linguagem, estes são reconhecidos apenas quando se coloca um caractere <code>u</code> (de Unicode) após a expressão regular.</p>
<table>
<tr><td><code>\p{L}</code></td><td>Qualquer letra</td>
</tr>
<tr><td><code>\p{N}</code></td><td>Qualquer caractere numérico</td>
</tr>
<tr><td><code>\p{P}</code></td><td>Qualquer caractere de pontuação</td>
</tr>
<tr><td><code>\P{L}</code></td><td>Qualquer não-letra (P maiúsculo inverte)</td>
</tr>
<tr><td><code>\p{Script=Hangul}</code></td><td>Qualquer caractere do script dado (veja <a href="05_higher_order.html#scripts">Capítulo 5</a>)</td>
</tr>
</table>
<p><a class="p_ident" id="p-OUg2MIscQ2" href="#p-OUg2MIscQ2" tabindex="-1" role="presentation"></a>Usar <code>\w</code> para processamento de texto que pode precisar lidar com texto não-inglês (ou até texto inglês com palavras emprestadas como “cliché") é um risco, pois não tratará caracteres como "é" como letras. Embora tendam a ser um pouco mais verbosos, os grupos de propriedades <code>\p</code> são mais robustos.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-s9MXvH0LEh" href="#c-s9MXvH0LEh" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string2">/\p{L}/u</span>.test(<span class="tok-string">"α"</span>));
<span class="tok-comment">// → true</span>
console.log(<span class="tok-string2">/\p{L}/u</span>.test(<span class="tok-string">"!"</span>));
<span class="tok-comment">// → false</span>
console.log(<span class="tok-string2">/\p{Script=Greek}/u</span>.test(<span class="tok-string">"α"</span>));
<span class="tok-comment">// → true</span>
console.log(<span class="tok-string2">/\p{Script=Arabic}/u</span>.test(<span class="tok-string">"α"</span>));
<span class="tok-comment">// → false</span></pre>
<p><a class="p_ident" id="p-vSQNN+HAuG" href="#p-vSQNN+HAuG" tabindex="-1" role="presentation"></a>Por outro lado, se você está correspondendo números para fazer algo com eles, frequentemente quer <code>\d</code> para dígitos, pois converter caracteres numéricos arbitrários em um número JavaScript não é algo que uma função como <code>Number</code> pode fazer por você.</p>
<h2><a class="h_ident" id="h-WaJdnoW1ZX" href="#h-WaJdnoW1ZX" tabindex="-1" role="presentation"></a>Repetindo partes de um padrão</h2>
<p><a class="p_ident" id="p-S3yopXi4Z9" href="#p-S3yopXi4Z9" tabindex="-1" role="presentation"></a>Agora sabemos como corresponder a um único dígito. E se quisermos corresponder a um número inteiro — uma sequência de um ou mais dígitos?</p>
<p><a class="p_ident" id="p-QU16RPD1Af" href="#p-QU16RPD1Af" tabindex="-1" role="presentation"></a>Quando você coloca um sinal de mais (<code>+</code>) após algo em uma expressão regular, isso indica que o elemento pode se repetir mais de uma vez. Assim, <code>/\d+/</code> corresponde a um ou mais caracteres de dígito.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-9/5mFF4Ih4" href="#c-9/5mFF4Ih4" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string2">/'\d+'/</span>.test(<span class="tok-string">"'123'"</span>));
<span class="tok-comment">// → true</span>
console.log(<span class="tok-string2">/'\d+'/</span>.test(<span class="tok-string">"''"</span>));
<span class="tok-comment">// → false</span>
console.log(<span class="tok-string2">/'\d*'/</span>.test(<span class="tok-string">"'123'"</span>));
<span class="tok-comment">// → true</span>
console.log(<span class="tok-string2">/'\d*'/</span>.test(<span class="tok-string">"''"</span>));
<span class="tok-comment">// → true</span></pre>
<p><a class="p_ident" id="p-v00aUBgr60" href="#p-v00aUBgr60" tabindex="-1" role="presentation"></a>O asterisco (<code>*</code>) tem um significado semelhante, mas também permite que o padrão corresponda zero vezes. Algo com um asterisco depois nunca impede que um padrão corresponda — ele simplesmente corresponderá zero instâncias se não encontrar nenhum texto adequado.</p>
<p><a class="p_ident" id="p-ntGeSUAIQG" href="#p-ntGeSUAIQG" tabindex="-1" role="presentation"></a>Uma interrogação (<code>?</code>) torna uma parte de um padrão <em>opcional</em>, significando que pode ocorrer zero vezes ou uma vez. No exemplo a seguir, o caractere <em>u</em> é permitido mas o padrão também corresponde quando ele está ausente:</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-EiCIowdq+d" href="#c-EiCIowdq+d" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">neighbor</span> = <span class="tok-string2">/neighbou?r/</span>;
console.log(neighbor.test(<span class="tok-string">"neighbour"</span>));
<span class="tok-comment">// → true</span>
console.log(neighbor.test(<span class="tok-string">"neighbor"</span>));
<span class="tok-comment">// → true</span></pre>
<p><a class="p_ident" id="p-jF75nfQFbh" href="#p-jF75nfQFbh" tabindex="-1" role="presentation"></a>Para indicar que um padrão deve ocorrer um número preciso de vezes, use chaves. Colocar <code>{4}</code> após um elemento, por exemplo, exige que ocorra exatamente quatro vezes. Também é possível especificar um intervalo dessa forma: <code>{2,4}</code> significa que o elemento deve ocorrer pelo menos duas vezes e no máximo quatro vezes.</p>
<p id="date_regexp_counted"><a class="p_ident" id="p-Cvfl7+Kyiw" href="#p-Cvfl7+Kyiw" tabindex="-1" role="presentation"></a>Aqui está outra versão do padrão de data e hora que permite tanto dígitos simples quanto duplos para dias, meses e horas. Também é um pouco mais fácil de decifrar.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-Tw+K6Mxe45" href="#c-Tw+K6Mxe45" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">dateTime</span> = <span class="tok-string2">/\d{1,2}-\d{1,2}-\d{4} \d{1,2}:\d{2}/</span>;
console.log(dateTime.test(<span class="tok-string">"1-30-2003 8:45"</span>));
<span class="tok-comment">// → true</span></pre>
<p><a class="p_ident" id="p-6G/p0wGoPg" href="#p-6G/p0wGoPg" tabindex="-1" role="presentation"></a>Você também pode especificar intervalos abertos ao usar chaves, omitindo o número após a vírgula. Por exemplo, <code>{5,}</code> significa cinco ou mais vezes.</p>
<h2><a class="h_ident" id="h-Tlkg1sH5uz" href="#h-Tlkg1sH5uz" tabindex="-1" role="presentation"></a>Agrupando subexpressões</h2>
<p><a class="p_ident" id="p-SLjn4DypgH" href="#p-SLjn4DypgH" tabindex="-1" role="presentation"></a>Para usar um operador como <code>*</code> ou <code>+</code> em mais de um elemento por vez, você deve usar parênteses. Uma parte de uma expressão regular entre parênteses conta como um único elemento para os operadores que a seguem.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-P/f6a65XwI" href="#c-P/f6a65XwI" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">cartoonCrying</span> = <span class="tok-string2">/boo+(hoo+)+/i</span>;
console.log(cartoonCrying.test(<span class="tok-string">"Boohoooohoohooo"</span>));
<span class="tok-comment">// → true</span></pre>
<p><a class="p_ident" id="p-bpnP9TkflO" href="#p-bpnP9TkflO" tabindex="-1" role="presentation"></a>O primeiro e o segundo <code>+</code> se aplicam apenas ao segundo <code>o</code> em <code>boo</code> e <code>hoo</code>, respectivamente. O terceiro <code>+</code> se aplica a todo o grupo <code>(hoo+)</code>, correspondendo a uma ou mais sequências assim.</p>
<p><a class="p_ident" id="p-Rx/dXHLU4e" href="#p-Rx/dXHLU4e" tabindex="-1" role="presentation"></a>O <code>i</code> no final da expressão no exemplo torna essa expressão regular insensível a maiúsculas e minúsculas, permitindo que corresponda ao <em>B</em> maiúsculo na <em>string</em> de entrada, mesmo que o padrão em si seja todo minúsculo.</p>
<h2><a class="h_ident" id="h-kDj/U5i/pm" href="#h-kDj/U5i/pm" tabindex="-1" role="presentation"></a>Correspondências e grupos</h2>
<p><a class="p_ident" id="p-xSFMQzjDaJ" href="#p-xSFMQzjDaJ" tabindex="-1" role="presentation"></a>O método <code>test</code> é a maneira absolutamente mais simples de corresponder uma expressão regular. Ele diz apenas se correspondeu e nada mais. Expressões regulares também possuem um método <code>exec</code> (executar) que retornará <code>null</code> se nenhuma correspondência foi encontrada e retornará um objeto com informações sobre a correspondência caso contrário.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-JJMWZpk0iD" href="#c-JJMWZpk0iD" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">match</span> = <span class="tok-string2">/\d+/</span>.exec(<span class="tok-string">"one two 100"</span>);
console.log(match);
<span class="tok-comment">// → ["100"]</span>
console.log(match.index);
<span class="tok-comment">// → 8</span></pre>
<p><a class="p_ident" id="p-kdm7M2LPMb" href="#p-kdm7M2LPMb" tabindex="-1" role="presentation"></a>Um objeto retornado de <code>exec</code> tem uma propriedade <code>index</code> que nos diz <em>onde</em> na <em>string</em> a correspondência bem-sucedida começa. Fora isso, o objeto parece (e de fato é) um <em>array</em> de <em>strings</em>, cujo primeiro elemento é a <em>string</em> que foi correspondida. No exemplo anterior, esta é a sequência de dígitos que procurávamos.</p>
<p><a class="p_ident" id="p-mhJJMtxPmp" href="#p-mhJJMtxPmp" tabindex="-1" role="presentation"></a>Valores de <em>string</em> têm um método <code>match</code> que se comporta de forma semelhante.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-uAkAqNYx+q" href="#c-uAkAqNYx+q" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string">"one two 100"</span>.match(<span class="tok-string2">/\d+/</span>));
<span class="tok-comment">// → ["100"]</span></pre>
<p><a class="p_ident" id="p-fW8B5mewyt" href="#p-fW8B5mewyt" tabindex="-1" role="presentation"></a>Quando a expressão regular contém subexpressões agrupadas com parênteses, o texto que correspondeu a esses grupos também aparecerá no <em>array</em>. A correspondência inteira é sempre o primeiro elemento. O próximo elemento é a parte correspondida pelo primeiro grupo (aquele cujo parêntese de abertura vem primeiro na expressão), depois o segundo grupo, e assim por diante.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-5E2M1BBsUm" href="#c-5E2M1BBsUm" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">quotedText</span> = <span class="tok-string2">/'([^']*)'/</span>;
console.log(quotedText.exec(<span class="tok-string">"she said 'hello'"</span>));
<span class="tok-comment">// → ["'hello'", "hello"]</span></pre>
<p><a class="p_ident" id="p-brR1i/joTH" href="#p-brR1i/joTH" tabindex="-1" role="presentation"></a>Quando um grupo não é correspondido de forma alguma (por exemplo, quando seguido por uma interrogação), sua posição no <em>array</em> de saída conterá <code>undefined</code>. Quando um grupo é correspondido múltiplas vezes (por exemplo, quando seguido por um <code>+</code>), apenas a última correspondência acaba no <em>array</em>.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-j9t+gn+1eT" href="#c-j9t+gn+1eT" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string2">/bad(ly)?/</span>.exec(<span class="tok-string">"bad"</span>));
<span class="tok-comment">// → ["bad", undefined]</span>
console.log(<span class="tok-string2">/(\d)+/</span>.exec(<span class="tok-string">"123"</span>));
<span class="tok-comment">// → ["123", "3"]</span></pre>
<p><a class="p_ident" id="p-BKcbVz1sFN" href="#p-BKcbVz1sFN" tabindex="-1" role="presentation"></a>Se você quiser usar parênteses puramente para agrupamento, sem que apareçam no <em>array</em> de correspondências, pode colocar <code>?:</code> após o parêntese de abertura.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-JYRtOm6gNP" href="#c-JYRtOm6gNP" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string2">/(?:na)+/</span>.exec(<span class="tok-string">"banana"</span>));
<span class="tok-comment">// → ["nana"]</span></pre>
<p><a class="p_ident" id="p-BHZdvIFXVh" href="#p-BHZdvIFXVh" tabindex="-1" role="presentation"></a>Grupos podem ser úteis para extrair partes de uma <em>string</em>. Se não queremos apenas verificar se uma <em>string</em> contém uma data, mas também extraí-la e construir um objeto que a represente, podemos envolver parênteses ao redor dos padrões de dígitos e extrair diretamente a data do resultado de <code>exec</code>.</p>
<p><a class="p_ident" id="p-hiupEjxpRT" href="#p-hiupEjxpRT" tabindex="-1" role="presentation"></a>Mas primeiro faremos um breve desvio para discutir a maneira embutida de representar valores de data e hora em JavaScript.</p>
<h2><a class="h_ident" id="h-fLRAZmjrML" href="#h-fLRAZmjrML" tabindex="-1" role="presentation"></a>A classe Date</h2>
<p><a class="p_ident" id="p-f3toj0NmJU" href="#p-f3toj0NmJU" tabindex="-1" role="presentation"></a>O JavaScript tem uma classe <code>Date</code> padrão para representar datas, ou melhor, pontos no tempo. Se você simplesmente criar um objeto date usando <code>new</code>, obtém a data e hora atuais.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-pkHhVmn87o" href="#c-pkHhVmn87o" tabindex="-1" role="presentation"></a>console.log(<span class="tok-keyword">new</span> Date());
<span class="tok-comment">// → Fri Feb 02 2024 18:03:06 GMT+0100 (CET)</span></pre>
<p><a class="p_ident" id="p-JoikQEpixO" href="#p-JoikQEpixO" tabindex="-1" role="presentation"></a>Você também pode criar um objeto para um momento específico.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-2VCU0f4HsQ" href="#c-2VCU0f4HsQ" tabindex="-1" role="presentation"></a>console.log(<span class="tok-keyword">new</span> Date(<span class="tok-number">2009</span>, <span class="tok-number">11</span>, <span class="tok-number">9</span>));
<span class="tok-comment">// → Wed Dec 09 2009 00:00:00 GMT+0100 (CET)</span>
console.log(<span class="tok-keyword">new</span> Date(<span class="tok-number">2009</span>, <span class="tok-number">11</span>, <span class="tok-number">9</span>, <span class="tok-number">12</span>, <span class="tok-number">59</span>, <span class="tok-number">59</span>, <span class="tok-number">999</span>));
<span class="tok-comment">// → Wed Dec 09 2009 12:59:59 GMT+0100 (CET)</span></pre>
<p><a class="p_ident" id="p-X2A7+JhwKL" href="#p-X2A7+JhwKL" tabindex="-1" role="presentation"></a>O JavaScript usa uma convenção onde os números dos meses começam em zero (então dezembro é 11), mas os números dos dias começam em um. Isso é confuso e ridículo. Tome cuidado.</p>
<p><a class="p_ident" id="p-U/qbvWVS2s" href="#p-U/qbvWVS2s" tabindex="-1" role="presentation"></a>Os últimos quatro argumentos (horas, minutos, segundos e milissegundos) são opcionais e considerados zero quando não fornecidos.</p>
<p><a class="p_ident" id="p-dmPUzsNPz5" href="#p-dmPUzsNPz5" tabindex="-1" role="presentation"></a><em>Timestamps</em> são armazenados como o número de milissegundos desde o início de 1970, no fuso horário UTC. Isso segue uma convenção estabelecida pelo “tempo Unix”, que foi inventado por volta daquela época. Você pode usar números negativos para tempos antes de 1970. O método <code>getTime</code> em um objeto date retorna esse número. É grande, como você pode imaginar.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-lMlCuckMIc" href="#c-lMlCuckMIc" tabindex="-1" role="presentation"></a>console.log(<span class="tok-keyword">new</span> Date(<span class="tok-number">2013</span>, <span class="tok-number">11</span>, <span class="tok-number">19</span>).getTime());
<span class="tok-comment">// → 1387407600000</span>
console.log(<span class="tok-keyword">new</span> Date(<span class="tok-number">1387407600000</span>));
<span class="tok-comment">// → Thu Dec 19 2013 00:00:00 GMT+0100 (CET)</span></pre>
<p><a class="p_ident" id="p-l/QNHJjb3Q" href="#p-l/QNHJjb3Q" tabindex="-1" role="presentation"></a>Se você der ao construtor <code>Date</code> um único argumento, esse argumento é tratado como tal contagem de milissegundos. Você pode obter a contagem de milissegundos atual criando um novo objeto <code>Date</code> e chamando <code>getTime</code> nele ou chamando a função <code>Date.now</code>.</p>
<p><a class="p_ident" id="p-AgGkd6tc4t" href="#p-AgGkd6tc4t" tabindex="-1" role="presentation"></a>Objetos Date fornecem métodos como <code>getFullYear</code>, <code>getMonth</code>, <code>getDate</code>, <code>getHours</code>, <code>getMinutes</code> e <code>getSeconds</code> para extrair seus componentes. Além de <code>getFullYear</code> há também <code>getYear</code>, que dá o ano menos 1900 (como <code>98</code> ou <code>125</code>) e é em grande parte inútil.</p>
<p><a class="p_ident" id="p-ZVyWV0zw3l" href="#p-ZVyWV0zw3l" tabindex="-1" role="presentation"></a>Colocando parênteses ao redor das partes da expressão que nos interessam, podemos agora criar um objeto date a partir de uma <em>string</em>.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-xW0xfMnpiZ" href="#c-xW0xfMnpiZ" tabindex="-1" role="presentation"></a><span class="tok-keyword">function</span> <span class="tok-definition">getDate</span>(<span class="tok-definition">string</span>) {
<span class="tok-keyword">let</span> [<span class="tok-definition">_</span>, <span class="tok-definition">month</span>, <span class="tok-definition">day</span>, <span class="tok-definition">year</span>] =
<span class="tok-string2">/(\d{1,2})-(\d{1,2})-(\d{4})/</span>.exec(string);
<span class="tok-keyword">return</span> <span class="tok-keyword">new</span> Date(year, month - <span class="tok-number">1</span>, day);
}
console.log(getDate(<span class="tok-string">"1-30-2003"</span>));
<span class="tok-comment">// → Thu Jan 30 2003 00:00:00 GMT+0100 (CET)</span></pre>
<p><a class="p_ident" id="p-B44jzDvBCf" href="#p-B44jzDvBCf" tabindex="-1" role="presentation"></a>A <em>binding</em> sublinhado (<code>_</code>) é ignorada e usada apenas para pular o elemento de correspondência completa no <em>array</em> retornado por <code>exec</code>.</p>
<h2><a class="h_ident" id="h-K1Koqikglo" href="#h-K1Koqikglo" tabindex="-1" role="presentation"></a>Limites e look-ahead</h2>
<p><a class="p_ident" id="p-RPdLAEd8LK" href="#p-RPdLAEd8LK" tabindex="-1" role="presentation"></a>Infelizmente, <code>getDate</code> também extrairá uma data da <em>string</em> <code>"100-1-30000"</code>. Uma correspondência pode acontecer em qualquer lugar na <em>string</em>, então neste caso, ela simplesmente começará no segundo caractere e terminará no penúltimo.</p>
<p><a class="p_ident" id="p-xNxPWmZm3v" href="#p-xNxPWmZm3v" tabindex="-1" role="presentation"></a>Se quisermos forçar que a correspondência abranja toda a <em>string</em>, podemos adicionar os marcadores <code>^</code> e <code>$</code>. O circunflexo corresponde ao início da <em>string</em> de entrada, enquanto o cifrão corresponde ao final. Assim, <code>/^\d+$/</code> corresponde a uma <em>string</em> consistindo inteiramente de um ou mais dígitos, <code>/^!/</code> corresponde a qualquer <em>string</em> que comece com ponto de exclamação, e <code>/x^/</code> não corresponde a nenhuma <em>string</em> (não pode haver um <code>x</code> antes do início da <em>string</em>).</p>
<p><a class="p_ident" id="p-9y37WZH9OY" href="#p-9y37WZH9OY" tabindex="-1" role="presentation"></a>Há também um marcador <code>\b</code> que corresponde a <em>limites de palavra</em>, posições que têm um caractere de palavra de um lado e um caractere não-palavra do outro. Infelizmente, estes usam o mesmo conceito simplista de caracteres de palavra que <code>\w</code> e, portanto, não são muito confiáveis.</p>
<p><a class="p_ident" id="p-q9IbyYMmUU" href="#p-q9IbyYMmUU" tabindex="-1" role="presentation"></a>Note que esses marcadores de limite não correspondem a nenhum caractere real. Eles apenas impõem que uma dada condição se mantenha no lugar onde aparecem no padrão.</p>
<p><a class="p_ident" id="p-cNo/ytarX/" href="#p-cNo/ytarX/" tabindex="-1" role="presentation"></a>Testes de <em>look-ahead</em> fazem algo semelhante. Eles fornecem um padrão e farão a correspondência falhar se a entrada não corresponder àquele padrão, mas não avançam a posição de correspondência. São escritos entre <code>(?=</code> e <code>)</code>.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-ryfOqCgM06" href="#c-ryfOqCgM06" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string2">/a(?=e)/</span>.exec(<span class="tok-string">"braeburn"</span>));
<span class="tok-comment">// → ["a"]</span>
console.log(<span class="tok-string2">/a(?! )/</span>.exec(<span class="tok-string">"a b"</span>));
<span class="tok-comment">// → null</span></pre>
<p><a class="p_ident" id="p-i8mFeQQ01+" href="#p-i8mFeQQ01+" tabindex="-1" role="presentation"></a>O <code>e</code> no primeiro exemplo é necessário para corresponder, mas não faz parte da <em>string</em> correspondida. A notação <code>(?! )</code> expressa um look-ahead <em>negativo</em>. Isso corresponde apenas se o padrão entre parênteses <em>não</em> corresponder, fazendo com que o segundo exemplo corresponda apenas a caracteres <code>a</code> que não tenham um espaço depois deles.</p>
<h2><a class="h_ident" id="h-MLINKpFTQe" href="#h-MLINKpFTQe" tabindex="-1" role="presentation"></a>Padrões de escolha</h2>
<p><a class="p_ident" id="p-VLZ3tevmL9" href="#p-VLZ3tevmL9" tabindex="-1" role="presentation"></a>Digamos que queremos saber se um trecho de texto contém não apenas um número, mas um número seguido de uma das palavras <em>pig</em>, <em>cow</em> ou <em>chicken</em>, ou qualquer uma de suas formas plurais.</p>
<p><a class="p_ident" id="p-eiUlQuWufb" href="#p-eiUlQuWufb" tabindex="-1" role="presentation"></a>Poderíamos escrever três expressões regulares e testá-las em sequência, mas há uma maneira mais elegante. O caractere pipe (<code>|</code>) denota uma escolha entre o padrão à sua esquerda e o padrão à sua direita. Podemos usá-lo em expressões como esta:</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-tEyQxhonoR" href="#c-tEyQxhonoR" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">animalCount</span> = <span class="tok-string2">/\d+ (pig|cow|chicken)s?/</span>;
console.log(animalCount.test(<span class="tok-string">"15 pigs"</span>));
<span class="tok-comment">// → true</span>
console.log(animalCount.test(<span class="tok-string">"15 pugs"</span>));
<span class="tok-comment">// → false</span></pre>
<p><a class="p_ident" id="p-Yd1zM8wLBD" href="#p-Yd1zM8wLBD" tabindex="-1" role="presentation"></a>Parênteses podem ser usados para limitar a parte do padrão à qual o operador pipe se aplica, e você pode colocar múltiplos desses operadores lado a lado para expressar uma escolha entre mais de duas alternativas.</p>
<h2><a class="h_ident" id="h-rn+Ozizj4v" href="#h-rn+Ozizj4v" tabindex="-1" role="presentation"></a>A mecânica da correspondência</h2>
<p><a class="p_ident" id="p-v0+rht8ZAM" href="#p-v0+rht8ZAM" tabindex="-1" role="presentation"></a>Conceitualmente, quando você usa <code>exec</code> ou <code>test</code>, o motor de expressão regular procura uma correspondência na sua <em>string</em> tentando corresponder a expressão primeiro a partir do início da <em>string</em>, depois a partir do segundo caractere, e assim por diante até encontrar uma correspondência ou alcançar o final da <em>string</em>. Ele retornará a primeira correspondência encontrada ou falhará em encontrar qualquer correspondência.</p>
<p><a class="p_ident" id="p-4WaT9mSvml" href="#p-4WaT9mSvml" tabindex="-1" role="presentation"></a>Para fazer a correspondência real, o motor trata uma expressão regular como um diagrama de fluxo. Este é o diagrama para a expressão de animais do exemplo anterior:</p><figure><img src="img/re_pigchickens.svg" alt="Railroad diagram that first passes through a box labeled 'digit', which has a loop going back from after it to before it, and then a box for a space character. After that, the railroad splits in three, going through boxes for 'pig', 'cow', and 'chicken'. After those it rejoins, and goes through a box labeled 's', which, being optional, also has a railroad that passes it by. Finally, the line reaches the accepting state."></figure>
<p><a class="p_ident" id="p-G0Ik4Z9d+h" href="#p-G0Ik4Z9d+h" tabindex="-1" role="presentation"></a>Se conseguirmos encontrar um caminho do lado esquerdo do diagrama ao lado direito, nossa expressão corresponde. Mantemos uma posição atual na <em>string</em> e, toda vez que passamos por uma caixa, verificamos que a parte da <em>string</em> após nossa posição atual corresponde àquela caixa.</p>
<h2 id="backtracking"><a class="h_ident" id="h-f2NK8AaM/E" href="#h-f2NK8AaM/E" tabindex="-1" role="presentation"></a>Retrocesso</h2>
<p><a class="p_ident" id="p-EOGq2JVwbt" href="#p-EOGq2JVwbt" tabindex="-1" role="presentation"></a>A expressão regular <code>/<wbr>^([01]+b|[\da-f]+h|\d+)$/<wbr></code> corresponde a um número binário seguido de um <code>b</code>, um número hexadecimal (isto é, base 16, com as letras <code>a</code> a <code>f</code> representando os dígitos 10 a 15) seguido de um <code>h</code>, ou um número decimal regular sem caractere de sufixo. Este é o diagrama correspondente:</p><figure><img src="img/re_number.svg" alt="Railroad diagram for the regular expression '^([01]+b|\d+|[\da-f]+h)$'"></figure>
<p><a class="p_ident" id="p-YSI4v8mB0H" href="#p-YSI4v8mB0H" tabindex="-1" role="presentation"></a>Ao corresponder essa expressão, o ramo superior (binário) frequentemente será tentado mesmo que a entrada não contenha um número binário. Ao corresponder a <em>string</em> <code>"103"</code>, por exemplo, fica claro apenas no <code>3</code> que estamos no ramo errado. A <em>string</em> <em>corresponde</em> à expressão, apenas não ao ramo em que estamos atualmente.</p>
<p><a class="p_ident" id="p-FvUx+xa/bj" href="#p-FvUx+xa/bj" tabindex="-1" role="presentation"></a>Então o motor <em>retrocede</em>. Ao entrar em um ramo, ele lembra sua posição atual (neste caso, no início da <em>string</em>, logo após a primeira caixa de limite no diagrama) para que possa voltar e tentar outro ramo se o atual não funcionar. Para a <em>string</em> <code>"103"</code>, após encontrar o caractere <code>3</code>, o motor começa a tentar o ramo para números hexadecimais, que falha novamente porque não há um <code>h</code> após o número. Então tenta o ramo de números decimais. Esse se encaixa, e uma correspondência é reportada.</p>
<p><a class="p_ident" id="p-etZ7hEU/2I" href="#p-etZ7hEU/2I" tabindex="-1" role="presentation"></a>O motor para assim que encontra uma correspondência completa. Isso significa que, se múltiplos ramos poderiam potencialmente corresponder a uma <em>string</em>, apenas o primeiro (ordenado por onde os ramos aparecem na expressão regular) é usado.</p>
<p><a class="p_ident" id="p-nt7OC1l1B4" href="#p-nt7OC1l1B4" tabindex="-1" role="presentation"></a>O retrocesso também acontece para operadores de repetição como + e <code>*</code>. Se você corresponder <code>/^.*x/</code> contra <code>"abcxe"</code>, a parte <code>.*</code> primeiro tentará consumir toda a <em>string</em>. O motor então perceberá que precisa de um <code>x</code> para corresponder ao padrão. Como não há <code>x</code> após o final da <em>string</em>, o operador asterisco tenta corresponder um caractere a menos. Mas o motor não encontra um <code>x</code> após <code>abcx</code> também, então retrocede novamente, correspondendo o operador asterisco apenas a <code>abc</code>. <em>Agora</em> ele encontra um <code>x</code> onde precisa e reporta uma correspondência bem-sucedida das posições 0 a 4.</p>
<p><a class="p_ident" id="p-x18Gm11a7S" href="#p-x18Gm11a7S" tabindex="-1" role="presentation"></a>É possível escrever expressões regulares que farão <em>muito</em> retrocesso. Esse problema ocorre quando um padrão pode corresponder a um trecho de entrada de muitas maneiras diferentes. Por exemplo, se ficarmos confusos ao escrever uma expressão regular de número binário, podemos acidentalmente escrever algo como <code>/([01]+)+b/</code>.</p><figure><img src="img/re_slow.svg" alt="Railroad diagram for the regular expression '([01]+)+b'"></figure>
<p><a class="p_ident" id="p-tQ48Ogjaik" href="#p-tQ48Ogjaik" tabindex="-1" role="presentation"></a>Se isso tentar corresponder a uma longa série de zeros e uns sem um caractere <em>b</em> no final, o motor primeiro percorre o <em>loop</em> interno até ficar sem dígitos. Então percebe que não há <code>b</code>, então retrocede uma posição, percorre o <em>loop</em> externo uma vez e desiste novamente, tentando retroceder do <em>loop</em> interno mais uma vez. Ele continuará tentando toda rota possível através desses dois <em>loops</em>. Isso significa que a quantidade de trabalho <em>dobra</em> com cada caractere adicional. Para apenas algumas dezenas de caracteres, a correspondência resultante levará praticamente uma eternidade.</p>
<h2><a class="h_ident" id="h-oCAab6tdC1" href="#h-oCAab6tdC1" tabindex="-1" role="presentation"></a>O método replace</h2>
<p><a class="p_ident" id="p-Rh8VgOatBp" href="#p-Rh8VgOatBp" tabindex="-1" role="presentation"></a>Valores de <em>string</em> possuem um método <code>replace</code> que pode ser usado para substituir parte da <em>string</em> por outra <em>string</em>.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-dPdIdK/Wyi" href="#c-dPdIdK/Wyi" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string">"papa"</span>.replace(<span class="tok-string">"p"</span>, <span class="tok-string">"m"</span>));
<span class="tok-comment">// → mapa</span></pre>
<p><a class="p_ident" id="p-jQi7R7GqmC" href="#p-jQi7R7GqmC" tabindex="-1" role="presentation"></a>O primeiro argumento também pode ser uma expressão regular, caso em que a primeira correspondência da expressão regular é substituída. Quando uma opção <code>g</code> (de <em>global</em>) é adicionada após a expressão regular, <em>todas</em> as correspondências na <em>string</em> serão substituídas, não apenas a primeira.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-ztGnSKyKy1" href="#c-ztGnSKyKy1" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string">"Borobudur"</span>.replace(<span class="tok-string2">/[ou]/</span>, <span class="tok-string">"a"</span>));
<span class="tok-comment">// → Barobudur</span>
console.log(<span class="tok-string">"Borobudur"</span>.replace(<span class="tok-string2">/[ou]/g</span>, <span class="tok-string">"a"</span>));
<span class="tok-comment">// → Barabadar</span></pre>
<p><a class="p_ident" id="p-HMrKAsh6xr" href="#p-HMrKAsh6xr" tabindex="-1" role="presentation"></a>O verdadeiro poder de usar expressões regulares com <code>replace</code> vem do fato de que podemos nos referir a grupos correspondidos na <em>string</em> de substituição. Por exemplo, digamos que temos uma <em>string</em> grande contendo nomes de pessoas, um nome por linha, no formato <code>Sobrenome, Nome</code>. Se quisermos trocar esses nomes e remover a vírgula para obter o formato <code>Nome Sobrenome</code>, podemos usar o seguinte código:</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-Kb7uk2ah9L" href="#c-Kb7uk2ah9L" tabindex="-1" role="presentation"></a>console.log(
<span class="tok-string">"Liskov, Barbara</span><span class="tok-string2">\n</span><span class="tok-string">McCarthy, John</span><span class="tok-string2">\n</span><span class="tok-string">Milner, Robin"</span>
.replace(<span class="tok-string2">/(\p{L}+), (\p{L}+)/gu</span>, <span class="tok-string">"$2 $1"</span>));
<span class="tok-comment">// → Barbara Liskov</span>
<span class="tok-comment">// John McCarthy</span>
<span class="tok-comment">// Robin Milner</span></pre>
<p><a class="p_ident" id="p-7Xpj8BZxZI" href="#p-7Xpj8BZxZI" tabindex="-1" role="presentation"></a>O <code>$1</code> e <code>$2</code> na <em>string</em> de substituição se referem aos grupos entre parênteses no padrão. <code>$1</code> é substituído pelo texto que correspondeu ao primeiro grupo, <code>$2</code> pelo segundo, e assim por diante, até <code>$9</code>. A correspondência inteira pode ser referenciada com <code>$&</code>.</p>
<p><a class="p_ident" id="p-7p/wwgtzKy" href="#p-7p/wwgtzKy" tabindex="-1" role="presentation"></a>É possível passar uma função — em vez de uma <em>string</em> — como segundo argumento de <code>replace</code>. Para cada substituição, a função será chamada com os grupos correspondidos (assim como a correspondência inteira) como argumentos, e seu valor de retorno será inserido na nova <em>string</em>.</p>
<p><a class="p_ident" id="p-qepFva9dnS" href="#p-qepFva9dnS" tabindex="-1" role="presentation"></a>Aqui está um exemplo:</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-Iz7uVI1dvs" href="#c-Iz7uVI1dvs" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">stock</span> = <span class="tok-string">"1 lemon, 2 cabbages, and 101 eggs"</span>;
<span class="tok-keyword">function</span> <span class="tok-definition">minusOne</span>(<span class="tok-definition">match</span>, <span class="tok-definition">amount</span>, <span class="tok-definition">unit</span>) {
amount = Number(amount) - <span class="tok-number">1</span>;
<span class="tok-keyword">if</span> (amount == <span class="tok-number">1</span>) { <span class="tok-comment">// só restou um, remover o 's'</span>
unit = unit.slice(<span class="tok-number">0</span>, unit.length - <span class="tok-number">1</span>);
} <span class="tok-keyword">else</span> <span class="tok-keyword">if</span> (amount == <span class="tok-number">0</span>) {
amount = <span class="tok-string">"no"</span>;
}
<span class="tok-keyword">return</span> amount + <span class="tok-string">" "</span> + unit;
}
console.log(stock.replace(<span class="tok-string2">/(\d+) (\p{L}+)/gu</span>, minusOne));
<span class="tok-comment">// → no lemon, 1 cabbage, and 100 eggs</span></pre>
<p><a class="p_ident" id="p-Iy4GX3L4WR" href="#p-Iy4GX3L4WR" tabindex="-1" role="presentation"></a>Este código pega uma <em>string</em>, encontra todas as ocorrências de um número seguido de uma palavra alfanumérica e retorna uma <em>string</em> que tem uma unidade a menos de cada quantidade.</p>
<p><a class="p_ident" id="p-aw4sETwPo8" href="#p-aw4sETwPo8" tabindex="-1" role="presentation"></a>O grupo <code>(\d+)</code> acaba como o argumento <code>amount</code> da função, e o grupo <code>(\p{L}+)</code> é vinculado a <code>unit</code>. A função converte <code>amount</code> para um número — o que sempre funciona, pois correspondeu a <code>\d+</code> anteriormente — e faz alguns ajustes caso reste apenas um ou zero.</p>
<h2><a class="h_ident" id="h-bEjqUrbw0m" href="#h-bEjqUrbw0m" tabindex="-1" role="presentation"></a>Ganância</h2>
<p><a class="p_ident" id="p-kWfrgmTMrV" href="#p-kWfrgmTMrV" tabindex="-1" role="presentation"></a>Podemos usar <code>replace</code> para escrever uma função que remove todos os comentários de um trecho de código JavaScript. Aqui está uma primeira tentativa:</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-u0oKSJTOA2" href="#c-u0oKSJTOA2" tabindex="-1" role="presentation"></a><span class="tok-keyword">function</span> <span class="tok-definition">stripComments</span>(<span class="tok-definition">code</span>) {
<span class="tok-keyword">return</span> code.replace(<span class="tok-string2">/\/\/.*|\/\*[^]*\*\//g</span>, <span class="tok-string">""</span>);
}
console.log(stripComments(<span class="tok-string">"1 + /* 2 */3"</span>));
<span class="tok-comment">// → 1 + 3</span>
console.log(stripComments(<span class="tok-string">"x = 10;// ten!"</span>));
<span class="tok-comment">// → x = 10;</span>
console.log(stripComments(<span class="tok-string">"1 /* a */+/* b */ 1"</span>));
<span class="tok-comment">// → 1 1</span></pre>
<p><a class="p_ident" id="p-mWS/tPAPFv" href="#p-mWS/tPAPFv" tabindex="-1" role="presentation"></a>A parte antes do operador <code>|</code> corresponde a duas barras seguidas de qualquer número de caracteres que não sejam nova linha. A parte para comentários de múltiplas linhas é mais complexa. Usamos <code>[^]</code> (qualquer caractere que não esteja no conjunto vazio de caracteres) como uma maneira de corresponder a qualquer caractere. Não podemos simplesmente usar um ponto aqui porque comentários de bloco podem continuar em uma nova linha, e o caractere ponto não corresponde a caracteres de nova linha.</p>
<p><a class="p_ident" id="p-VFy+RU+pCC" href="#p-VFy+RU+pCC" tabindex="-1" role="presentation"></a>Mas a saída da última linha parece ter dado errado. Por quê?</p>
<p><a class="p_ident" id="p-+tp2TSP9ZO" href="#p-+tp2TSP9ZO" tabindex="-1" role="presentation"></a>A parte <code>[^]*</code> da expressão, como descrevi na seção sobre retrocesso, primeiro tentará corresponder o máximo possível. Se isso fizer a próxima parte do padrão falhar, o motor volta um caractere e tenta novamente de lá. No exemplo, o motor primeiro tenta corresponder todo o restante da <em>string</em> e então volta de lá. Ele encontrará uma ocorrência de <code>*/</code> após voltar quatro caracteres e corresponder a isso. Não é isso que queríamos — a intenção era corresponder a um único comentário, não ir até o final do código e encontrar o final do último comentário de bloco.</p>
<p><a class="p_ident" id="p-LRwb5G1v94" href="#p-LRwb5G1v94" tabindex="-1" role="presentation"></a>Por causa desse comportamento, dizemos que os operadores de repetição (<code>+</code>, <code>*</code>, <code>?</code> e <code>{}</code>) são <em>gananciosos</em>, significando que correspondem o máximo possível e retrocedem a partir daí. Se você colocar uma interrogação após eles (<code>+?</code>, <code>*?</code>, <code>??</code>, <code>{}?</code>), eles se tornam não-gananciosos e começam correspondendo o mínimo possível, correspondendo mais apenas quando o padrão restante não se encaixa na correspondência menor.</p>
<p><a class="p_ident" id="p-/ouIxdeqBH" href="#p-/ouIxdeqBH" tabindex="-1" role="presentation"></a>E é exatamente isso que queremos neste caso. Fazendo o asterisco corresponder o menor trecho de caracteres que nos leve a um <code>*/</code>, consumimos um comentário de bloco e nada mais.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-MCNF7GxfR1" href="#c-MCNF7GxfR1" tabindex="-1" role="presentation"></a><span class="tok-keyword">function</span> <span class="tok-definition">stripComments</span>(<span class="tok-definition">code</span>) {
<span class="tok-keyword">return</span> code.replace(<span class="tok-string2">/\/\/.*|\/\*[^]*?\*\//g</span>, <span class="tok-string">""</span>);
}
console.log(stripComments(<span class="tok-string">"1 /* a */+/* b */ 1"</span>));
<span class="tok-comment">// → 1 + 1</span></pre>
<p><a class="p_ident" id="p-xAhPXnAkxO" href="#p-xAhPXnAkxO" tabindex="-1" role="presentation"></a>Muitos bugs em programas de expressão regular podem ser rastreados ao uso não intencional de um operador ganancioso onde um não-ganancioso funcionaria melhor. Ao usar um operador de repetição, prefira a variante não-gananciosa.</p>
<h2><a class="h_ident" id="h-h7vYu353k/" href="#h-h7vYu353k/" tabindex="-1" role="presentation"></a>Criando objetos RegExp dinamicamente</h2>
<p><a class="p_ident" id="p-lpEsgI4ClB" href="#p-lpEsgI4ClB" tabindex="-1" role="presentation"></a>Em alguns casos, você pode não saber o padrão exato que precisa corresponder quando está escrevendo seu código. Digamos que quer testar o nome do usuário em um trecho de texto. Você pode montar uma <em>string</em> e usar o construtor <code>RegExp</code> nela.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-czY//Kr9WF" href="#c-czY//Kr9WF" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">name</span> = <span class="tok-string">"harry"</span>;
<span class="tok-keyword">let</span> <span class="tok-definition">regexp</span> = <span class="tok-keyword">new</span> RegExp(<span class="tok-string">"(^|</span><span class="tok-string2">\\</span><span class="tok-string">s)"</span> + name + <span class="tok-string">"($|</span><span class="tok-string2">\\</span><span class="tok-string">s)"</span>, <span class="tok-string">"gi"</span>);
console.log(regexp.test(<span class="tok-string">"Harry is a dodgy character."</span>));
<span class="tok-comment">// → true</span></pre>
<p><a class="p_ident" id="p-Xw/tyhJM9w" href="#p-Xw/tyhJM9w" tabindex="-1" role="presentation"></a>Ao criar a parte <code>\s</code> da <em>string</em>, precisamos usar duas barras invertidas porque estamos escrevendo em uma <em>string</em> normal, não em uma expressão regular delimitada por barras. O segundo argumento do construtor <code>RegExp</code> contém as opções para a expressão regular — neste caso, <code>"gi"</code> para global e insensível a maiúsculas.</p>
<p><a class="p_ident" id="p-icRg04flzf" href="#p-icRg04flzf" tabindex="-1" role="presentation"></a>Mas e se o nome for <code>"dea+hl[]rd"</code> porque nosso usuário é um adolescente nerd? Isso resultaria em uma expressão regular sem sentido que não corresponderá ao nome do usuário.</p>
<p><a class="p_ident" id="p-JgBoJaRUxh" href="#p-JgBoJaRUxh" tabindex="-1" role="presentation"></a>Para contornar isso, podemos adicionar barras invertidas antes de qualquer caractere que tenha significado especial.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-5G9sdHuCiC" href="#c-5G9sdHuCiC" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">name</span> = <span class="tok-string">"dea+hl[]rd"</span>;
<span class="tok-keyword">let</span> <span class="tok-definition">escaped</span> = name.replace(<span class="tok-string2">/[\\[.+*?(){|^$]/g</span>, <span class="tok-string">"</span><span class="tok-string2">\\</span><span class="tok-string">$&"</span>);
<span class="tok-keyword">let</span> <span class="tok-definition">regexp</span> = <span class="tok-keyword">new</span> RegExp(<span class="tok-string">"(^|</span><span class="tok-string2">\\</span><span class="tok-string">s)"</span> + escaped + <span class="tok-string">"($|</span><span class="tok-string2">\\</span><span class="tok-string">s)"</span>,
<span class="tok-string">"gi"</span>);
<span class="tok-keyword">let</span> <span class="tok-definition">text</span> = <span class="tok-string">"This dea+hl[]rd guy is super annoying."</span>;
console.log(regexp.test(text));
<span class="tok-comment">// → true</span></pre>
<h2><a class="h_ident" id="h-rFRkJCtsD0" href="#h-rFRkJCtsD0" tabindex="-1" role="presentation"></a>O método search</h2>
<p><a class="p_ident" id="p-wHxSbdLzMI" href="#p-wHxSbdLzMI" tabindex="-1" role="presentation"></a>Embora o método <code>indexOf</code> em <em>strings</em> não possa ser chamado com uma expressão regular, existe outro método, <code>search</code>, que espera uma expressão regular. Como <code>indexOf</code>, ele retorna o primeiro índice no qual a expressão foi encontrada, ou -1 quando não foi encontrada.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-diUfxE6ifs" href="#c-diUfxE6ifs" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string">" word"</span>.search(<span class="tok-string2">/\S/</span>));
<span class="tok-comment">// → 2</span>
console.log(<span class="tok-string">" "</span>.search(<span class="tok-string2">/\S/</span>));
<span class="tok-comment">// → -1</span></pre>
<p><a class="p_ident" id="p-z+QBC7JjVU" href="#p-z+QBC7JjVU" tabindex="-1" role="presentation"></a>Infelizmente, não há como indicar que a correspondência deve começar em um dado deslocamento (como podemos com o segundo argumento de <code>indexOf</code>), o que seria frequentemente útil.</p>
<h2><a class="h_ident" id="h-+68nuIvkFR" href="#h-+68nuIvkFR" tabindex="-1" role="presentation"></a>A propriedade lastIndex</h2>
<p><a class="p_ident" id="p-pSQ5Kt6n/V" href="#p-pSQ5Kt6n/V" tabindex="-1" role="presentation"></a>O método <code>exec</code> similarmente não fornece uma maneira conveniente de começar a busca a partir de uma dada posição na <em>string</em>. Mas fornece uma maneira <em>in</em>conveniente.</p>
<p><a class="p_ident" id="p-4lbC5Fmm1G" href="#p-4lbC5Fmm1G" tabindex="-1" role="presentation"></a>Objetos de expressão regular possuem propriedades. Uma dessas propriedades é <code>source</code>, que contém a <em>string</em> a partir da qual a expressão foi criada. Outra propriedade é <code>lastIndex</code>, que controla, em algumas circunstâncias limitadas, onde a próxima correspondência começará.</p>
<p><a class="p_ident" id="p-iN+dC+2Um9" href="#p-iN+dC+2Um9" tabindex="-1" role="presentation"></a>Essas circunstâncias são que a expressão regular deve ter a opção global (<code>g</code>) ou sticky (<code>y</code>) habilitada, e a correspondência deve acontecer através do método <code>exec</code>. Novamente, uma solução menos confusa teria sido simplesmente permitir que um argumento extra fosse passado a <code>exec</code>, mas confusão é uma característica essencial da interface de expressões regulares do JavaScript.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-nXsHtqIJdF" href="#c-nXsHtqIJdF" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">pattern</span> = <span class="tok-string2">/y/g</span>;
pattern.lastIndex = <span class="tok-number">3</span>;
<span class="tok-keyword">let</span> <span class="tok-definition">match</span> = pattern.exec(<span class="tok-string">"xyzzy"</span>);
console.log(match.index);
<span class="tok-comment">// → 4</span>
console.log(pattern.lastIndex);
<span class="tok-comment">// → 5</span></pre>
<p><a class="p_ident" id="p-zMi4TTBcKb" href="#p-zMi4TTBcKb" tabindex="-1" role="presentation"></a>Se a correspondência foi bem-sucedida, a chamada a <code>exec</code> atualiza automaticamente a propriedade <code>lastIndex</code> para apontar após a correspondência. Se nenhuma correspondência foi encontrada, <code>lastIndex</code> é redefinido para 0, que também é o valor que tem em um objeto de expressão regular recém-construído.</p>
<p><a class="p_ident" id="p-fXtKrdnBrf" href="#p-fXtKrdnBrf" tabindex="-1" role="presentation"></a>A diferença entre as opções global e sticky é que, quando sticky está habilitada, a correspondência só terá sucesso se começar diretamente em <code>lastIndex</code>, enquanto com global, ela buscará adiante por uma posição onde uma correspondência possa começar.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-98GwGRIMj8" href="#c-98GwGRIMj8" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">global</span> = <span class="tok-string2">/abc/g</span>;
console.log(global.exec(<span class="tok-string">"xyz abc"</span>));
<span class="tok-comment">// → ["abc"]</span>
<span class="tok-keyword">let</span> <span class="tok-definition">sticky</span> = <span class="tok-string2">/abc/y</span>;
console.log(sticky.exec(<span class="tok-string">"xyz abc"</span>));
<span class="tok-comment">// → null</span></pre>
<p><a class="p_ident" id="p-D/n2NzIYBR" href="#p-D/n2NzIYBR" tabindex="-1" role="presentation"></a>Ao usar um valor de expressão regular compartilhado para múltiplas chamadas <code>exec</code>, essas atualizações automáticas na propriedade <code>lastIndex</code> podem causar problemas. Sua expressão regular pode estar acidentalmente começando em um índice deixado por uma chamada anterior.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-wrx2wO0P8M" href="#c-wrx2wO0P8M" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">digit</span> = <span class="tok-string2">/\d/g</span>;
console.log(digit.exec(<span class="tok-string">"here it is: 1"</span>));
<span class="tok-comment">// → ["1"]</span>
console.log(digit.exec(<span class="tok-string">"and now: 1"</span>));
<span class="tok-comment">// → null</span></pre>
<p><a class="p_ident" id="p-6ssPrPB2O9" href="#p-6ssPrPB2O9" tabindex="-1" role="presentation"></a>Outro efeito interessante da opção global é que ela muda o funcionamento do método <code>match</code> em <em>strings</em>. Quando chamado com uma expressão global, em vez de retornar um <em>array</em> semelhante ao retornado por <code>exec</code>, <code>match</code> encontrará <em>todas</em> as correspondências do padrão na <em>string</em> e retornará um <em>array</em> contendo as <em>strings</em> correspondidas.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-weT/d5+8vE" href="#c-weT/d5+8vE" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string">"Banana"</span>.match(<span class="tok-string2">/an/g</span>));
<span class="tok-comment">// → ["an", "an"]</span></pre>
<p><a class="p_ident" id="p-noX9UDahbf" href="#p-noX9UDahbf" tabindex="-1" role="presentation"></a>Portanto, tenha cuidado com expressões regulares globais. Os casos em que são necessárias — chamadas a <code>replace</code> e lugares onde você quer usar <code>lastIndex</code> explicitamente — são tipicamente as situações onde você quer usá-las.</p>
<p><a class="p_ident" id="p-slGKYGwYhs" href="#p-slGKYGwYhs" tabindex="-1" role="presentation"></a>Uma coisa comum a fazer é encontrar todas as correspondências de uma expressão regular em uma <em>string</em>. Podemos fazer isso usando o método <code>matchAll</code>.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-ZUQzV/Hnst" href="#c-ZUQzV/Hnst" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">input</span> = <span class="tok-string">"A string with 3 numbers in it... 42 and 88."</span>;
<span class="tok-keyword">let</span> <span class="tok-definition">matches</span> = input.matchAll(<span class="tok-string2">/\d+/g</span>);
<span class="tok-keyword">for</span> (<span class="tok-keyword">let</span> <span class="tok-definition">match</span> <span class="tok-keyword">of</span> matches) {
console.log(<span class="tok-string">"Found"</span>, match[<span class="tok-number">0</span>], <span class="tok-string">"at"</span>, match.index);
}
<span class="tok-comment">// → Found 3 at 14</span>
<span class="tok-comment">// Found 42 at 33</span>
<span class="tok-comment">// Found 88 at 40</span></pre>
<p><a class="p_ident" id="p-df+sniF62v" href="#p-df+sniF62v" tabindex="-1" role="presentation"></a>Este método retorna um <em>array</em> de <em>arrays</em> de correspondência. A expressão regular passada a <code>matchAll</code> <em>deve</em> ter <code>g</code> habilitado.</p>
<h2 id="ini"><a class="h_ident" id="h-uyJ1KZScPp" href="#h-uyJ1KZScPp" tabindex="-1" role="presentation"></a>Analisando um arquivo INI</h2>
<p><a class="p_ident" id="p-DAAfyYwL/T" href="#p-DAAfyYwL/T" tabindex="-1" role="presentation"></a>Para concluir o capítulo, veremos um problema que exige expressões regulares. Imagine que estamos escrevendo um programa para coletar automaticamente informações sobre nossos inimigos da internet. (Na verdade, não escreveremos esse programa aqui, apenas a parte que lê o arquivo de configuração. Desculpe.) O arquivo de configuração se parece com isto:</p>
<pre class="snippet" data-language="null" ><a class="c_ident" id="c-X0Xu9SWllT" href="#c-X0Xu9SWllT" tabindex="-1" role="presentation"></a>searchengine=https://duckduckgo.com/?q=$1
spitefulness=9.7
; comentários são precedidos por ponto e vírgula...
; cada seção diz respeito a um inimigo individual
[larry]
fullname=Larry Doe
type=kindergarten bully
website=http://www.geocities.com/CapeCanaveral/11451
[davaeorn]
fullname=Davaeorn
type=evil wizard
outputdir=/home/marijn/enemies/davaeorn</pre>
<p><a class="p_ident" id="p-ccU9PUwDpT" href="#p-ccU9PUwDpT" tabindex="-1" role="presentation"></a>As regras exatas desse formato — que é um formato de arquivo amplamente usado, geralmente chamado de arquivo <em>INI</em> — são as seguintes:</p>
<ul>
<li>
<p><a class="p_ident" id="p-eD+3n4SJ6g" href="#p-eD+3n4SJ6g" tabindex="-1" role="presentation"></a>Linhas em branco e linhas começando com ponto e vírgula são ignoradas.</p></li>
<li>
<p><a class="p_ident" id="p-3HNjXAgqV9" href="#p-3HNjXAgqV9" tabindex="-1" role="presentation"></a>Linhas entre <code>[</code> e <code>]</code> iniciam uma nova seção.</p></li>
<li>
<p><a class="p_ident" id="p-GxcWvQWuab" href="#p-GxcWvQWuab" tabindex="-1" role="presentation"></a>Linhas contendo um identificador alfanumérico seguido de um caractere <code>=</code> adicionam uma configuração à seção atual.</p></li>
<li>
<p><a class="p_ident" id="p-2/8uvIJ5S+" href="#p-2/8uvIJ5S+" tabindex="-1" role="presentation"></a>Qualquer outra coisa é inválida.</p></li></ul>
<p><a class="p_ident" id="p-Qy909n7iuh" href="#p-Qy909n7iuh" tabindex="-1" role="presentation"></a>Nossa tarefa é converter uma <em>string</em> como esta em um objeto cujas propriedades armazenem <em>strings</em> para configurações escritas antes do primeiro cabeçalho de seção e subobjetos para seções, com esses subobjetos armazenando as configurações da seção.</p>
<p><a class="p_ident" id="p-JspR5K/nyN" href="#p-JspR5K/nyN" tabindex="-1" role="presentation"></a>Como o formato precisa ser processado linha por linha, dividir o arquivo em linhas separadas é um bom começo. Vimos o método <code>split</code> no <a href="04_data.html#split">Capítulo 4</a>. Alguns sistemas operacionais, porém, usam não apenas um caractere de nova linha para separar linhas, mas um caractere de retorno de carro seguido de uma nova linha (<code>"\r\n"</code>). Dado que o método <code>split</code> também aceita uma expressão regular como argumento, podemos usar uma expressão regular como <code>/\r?\n/</code> para dividir de uma maneira que permita tanto <code>"\n"</code> quanto <code>"\r\n"</code> entre linhas.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-2HTpmFhTOC" href="#c-2HTpmFhTOC" tabindex="-1" role="presentation"></a><span class="tok-keyword">function</span> <span class="tok-definition">parseINI</span>(<span class="tok-definition">string</span>) {
<span class="tok-comment">// Começar com um objeto para armazenar os campos de nível superior</span>
<span class="tok-keyword">let</span> <span class="tok-definition">result</span> = {};
<span class="tok-keyword">let</span> <span class="tok-definition">section</span> = result;
<span class="tok-keyword">for</span> (<span class="tok-keyword">let</span> <span class="tok-definition">line</span> <span class="tok-keyword">of</span> string.split(<span class="tok-string2">/\r?\n/</span>)) {
<span class="tok-keyword">let</span> <span class="tok-definition">match</span>;
<span class="tok-keyword">if</span> (match = line.match(<span class="tok-string2">/^(\w+)=(.*)$/</span>)) {
section[match[<span class="tok-number">1</span>]] = match[<span class="tok-number">2</span>];
} <span class="tok-keyword">else</span> <span class="tok-keyword">if</span> (match = line.match(<span class="tok-string2">/^\[(.*)\]$/</span>)) {
section = result[match[<span class="tok-number">1</span>]] = {};
} <span class="tok-keyword">else</span> <span class="tok-keyword">if</span> (!<span class="tok-string2">/^\s*(;|$)/</span>.test(line)) {
<span class="tok-keyword">throw</span> <span class="tok-keyword">new</span> Error(<span class="tok-string">"Line '"</span> + line + <span class="tok-string">"' is not valid."</span>);
}
};
<span class="tok-keyword">return</span> result;
}
console.log(parseINI(<span class="tok-string2">`</span>
<span class="tok-string2">name=Vasilis</span>
<span class="tok-string2">[address]</span>
<span class="tok-string2">city=Tessaloniki`</span>));
<span class="tok-comment">// → {name: "Vasilis", address: {city: "Tessaloniki"}}</span></pre>
<p><a class="p_ident" id="p-Woaw7slTc9" href="#p-Woaw7slTc9" tabindex="-1" role="presentation"></a>O código percorre as linhas do arquivo e constrói um objeto. Propriedades no topo são armazenadas diretamente nesse objeto, enquanto propriedades encontradas em seções são armazenadas em um objeto de seção separado. A <em>binding</em> <code>section</code> aponta para o objeto da seção atual.</p>
<p><a class="p_ident" id="p-AusFIll05d" href="#p-AusFIll05d" tabindex="-1" role="presentation"></a>Há dois tipos de linhas significativas — cabeçalhos de seção ou linhas de propriedade. Quando uma linha é uma propriedade regular, ela é armazenada na seção atual. Quando é um cabeçalho de seção, um novo objeto de seção é criado e <code>section</code> é definido para apontar para ele.</p>
<p><a class="p_ident" id="p-R27qdYCmEr" href="#p-R27qdYCmEr" tabindex="-1" role="presentation"></a>Note o uso recorrente de <code>^</code> e <code>$</code> para garantir que a expressão corresponda à linha inteira, não apenas parte dela. Omitir esses resulta em código que funciona na maior parte, mas se comporta de forma estranha para certas entradas, o que pode ser um <em>bug</em> difícil de rastrear.</p>
<p><a class="p_ident" id="p-OpsplxibCx" href="#p-OpsplxibCx" tabindex="-1" role="presentation"></a>O padrão <code>if (match = string.<wbr>match(.<wbr>.<wbr>.<wbr>))</code> faz uso do fato de que o valor de uma expressão de atribuição (<code>=</code>) é o valor atribuído. Você frequentemente não tem certeza de que sua chamada a <code>match</code> terá sucesso, então só pode acessar o objeto resultante dentro de uma declaração <code>if</code> que testa isso. Para não quebrar a cadeia agradável de formas <code>else if</code>, atribuímos o resultado da correspondência a uma <em>binding</em> e imediatamente usamos essa atribuição como o teste da declaração <code>if</code>.</p>
<p><a class="p_ident" id="p-n31xQjpRVF" href="#p-n31xQjpRVF" tabindex="-1" role="presentation"></a>Se uma linha não é um cabeçalho de seção nem uma propriedade, a função verifica se é um comentário ou uma linha vazia usando a expressão <code>/^\s*(;|$)/</code> para corresponder a linhas que contêm apenas espaço em branco, ou espaço em branco seguido de ponto e vírgula (tornando o restante da linha um comentário). Quando uma linha não corresponde a nenhuma das formas esperadas, a função lança uma exceção.</p>
<h2><a class="h_ident" id="h-+8Lllxi+4Y" href="#h-+8Lllxi+4Y" tabindex="-1" role="presentation"></a>Unidades de código e caracteres</h2>
<p><a class="p_ident" id="p-FOmcLg75ki" href="#p-FOmcLg75ki" tabindex="-1" role="presentation"></a>Outro erro de design que foi padronizado nas expressões regulares do JavaScript é que, por padrão, operadores como <code>.</code> ou <code>?</code> trabalham com unidades de código (como discutido no <a href="05_higher_order.html#code_units">Capítulo 5</a>), não caracteres reais. Isso significa que caracteres compostos por duas unidades de código se comportam de maneira estranha.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-CfMTYxun8D" href="#c-CfMTYxun8D" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string2">/🍎{3}/</span>.test(<span class="tok-string">"🍎🍎🍎"</span>));
<span class="tok-comment">// → false</span>
console.log(<span class="tok-string2">/<.>/</span>.test(<span class="tok-string">"<🌹>"</span>));
<span class="tok-comment">// → false</span>
console.log(<span class="tok-string2">/<.>/u</span>.test(<span class="tok-string">"<🌹>"</span>));
<span class="tok-comment">// → true</span></pre>
<p><a class="p_ident" id="p-XwvWxN9v26" href="#p-XwvWxN9v26" tabindex="-1" role="presentation"></a>O problema é que o 🍎 na primeira linha é tratado como duas unidades de código, e <code>{3}</code> é aplicado apenas à segunda unidade. Da mesma forma, o ponto corresponde a uma única unidade de código, não às duas que compõem o emoji da rosa.</p>
<p><a class="p_ident" id="p-3vCT3gPCbL" href="#p-3vCT3gPCbL" tabindex="-1" role="presentation"></a>Você deve adicionar a opção <code>u</code> (Unicode) à sua expressão regular para fazê-la tratar tais caracteres adequadamente.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-3HTiC3yQKP" href="#c-3HTiC3yQKP" tabindex="-1" role="presentation"></a>console.log(<span class="tok-string2">/🍎{3}/u</span>.test(<span class="tok-string">"🍎🍎🍎"</span>));
<span class="tok-comment">// → true</span></pre>
<h2 id="summary_regexp"><a class="h_ident" id="h-741R75mvqx" href="#h-741R75mvqx" tabindex="-1" role="presentation"></a>Resumo</h2>
<p><a class="p_ident" id="p-RNFTJj+oG8" href="#p-RNFTJj+oG8" tabindex="-1" role="presentation"></a>Expressões regulares são objetos que representam padrões em <em>strings</em>. Elas usam sua própria linguagem para expressar esses padrões.</p>
<table>
<tr><td><code>/abc/</code></td><td>Uma sequência de caracteres</td>
</tr>
<tr><td><code>/[abc]/</code></td><td>Qualquer caractere de um conjunto</td>
</tr>
<tr><td><code>/[^abc]/</code></td><td>Qualquer caractere <em>não</em> em um conjunto</td>
</tr>
<tr><td><code>/[0-9]/</code></td><td>Qualquer caractere em um intervalo</td>
</tr>
<tr><td><code>/x+/</code></td><td>Uma ou mais ocorrências do padrão <code>x</code></td>
</tr>
<tr><td><code>/x+?/</code></td><td>Uma ou mais ocorrências, não-ganancioso</td>
</tr>
<tr><td><code>/x*/</code></td><td>Zero ou mais ocorrências</td>
</tr>
<tr><td><code>/x?/</code></td><td>Zero ou uma ocorrência</td>
</tr>
<tr><td><code>/x{2,4}/</code></td><td>Duas a quatro ocorrências</td>
</tr>
<tr><td><code>/(abc)/</code></td><td>Um grupo</td>
</tr>
<tr><td><code>/a|b|c/</code></td><td>Qualquer um de vários padrões</td>
</tr>
<tr><td><code>/\d/</code></td><td>Qualquer caractere de dígito</td>
</tr>
<tr><td><code>/\w/</code></td><td>Um caractere alfanumérico (“caractere de palavra”)</td>
</tr>
<tr><td><code>/\s/</code></td><td>Qualquer caractere de espaço em branco</td>
</tr>
<tr><td><code>/./</code></td><td>Qualquer caractere exceto nova linha</td>
</tr>
<tr><td><code>/\p{L}/u</code></td><td>Qualquer caractere de letra</td>
</tr>
<tr><td><code>/^/</code></td><td>Início da entrada</td>
</tr>
<tr><td><code>/$/</code></td><td>Fim da entrada</td>
</tr>
<tr><td><code>/(?=a)/</code></td><td>Um teste de look-ahead</td>
</tr>
</table>
<p><a class="p_ident" id="p-iAcyZjhVRk" href="#p-iAcyZjhVRk" tabindex="-1" role="presentation"></a>Uma expressão regular tem um método <code>test</code> para testar se uma dada <em>string</em> a corresponde. Ela também tem um método <code>exec</code> que, quando uma correspondência é encontrada, retorna um <em>array</em> contendo todos os grupos correspondidos. Tal <em>array</em> tem uma propriedade <code>index</code> que indica onde a correspondência começou.</p>
<p><a class="p_ident" id="p-yUyAa30ehJ" href="#p-yUyAa30ehJ" tabindex="-1" role="presentation"></a><em>Strings</em> têm um método <code>match</code> para corresponder contra uma expressão regular e um método <code>search</code> para buscar por uma, retornando apenas a posição inicial da correspondência. Seu método <code>replace</code> pode substituir correspondências de um padrão por uma <em>string</em> de substituição ou função.</p>
<p><a class="p_ident" id="p-vpWXbKxOdk" href="#p-vpWXbKxOdk" tabindex="-1" role="presentation"></a>Expressões regulares podem ter opções, que são escritas após a barra de fechamento. A opção <code>i</code> torna a correspondência insensível a maiúsculas e minúsculas. A opção <code>g</code> torna a expressão <em>global</em>, o que, entre outras coisas, faz com que o método <code>replace</code> substitua todas as instâncias em vez de apenas a primeira. A opção <code>y</code> torna uma expressão sticky, o que significa que ela não buscará adiante e pulará parte da <em>string</em> ao procurar uma correspondência. A opção <code>u</code> ativa o modo Unicode, que habilita a sintaxe <code>\p</code> e corrige diversos problemas em torno do tratamento de caracteres que ocupam duas unidades de código.</p>
<p><a class="p_ident" id="p-cIi/cr47EO" href="#p-cIi/cr47EO" tabindex="-1" role="presentation"></a>Expressões regulares são uma ferramenta afiada com um cabo estranho. Elas simplificam algumas tarefas tremendamente, mas podem rapidamente se tornar incontroláveis quando aplicadas a problemas complexos. Parte de saber como usá-las é resistir à vontade de tentar encaixar nelas coisas que elas não conseguem expressar de forma limpa.</p>
<h2><a class="h_ident" id="h-0CpJUZuhQJ" href="#h-0CpJUZuhQJ" tabindex="-1" role="presentation"></a>Exercícios</h2>
<p><a class="p_ident" id="p-3+TK0QmPZj" href="#p-3+TK0QmPZj" tabindex="-1" role="presentation"></a>É quase inevitável que, no decorrer do trabalho com esses exercícios, você ficará confuso e frustrado com algum comportamento inexplicável de expressão regular. Às vezes ajuda inserir sua expressão em uma ferramenta online como <a href="https://www.debuggex.com"><em>debuggex.com</em></a> para ver se sua visualização corresponde ao que você pretendia e experimentar como ela responde a várias <em>strings</em> de entrada.</p>
<h3><a class="i_ident" id="i-7rW1+pAh4L" href="#i-7rW1+pAh4L" tabindex="-1" role="presentation"></a>Golfe de regexp</h3>
<p><a class="p_ident" id="p-MX20IvA8oL" href="#p-MX20IvA8oL" tabindex="-1" role="presentation"></a><em>Golfe de código</em> é um termo usado para o jogo de tentar expressar um programa particular no menor número de caracteres possível. Da mesma forma, <em>golfe de regexp</em> é a prática de escrever a expressão regular mais curta possível para corresponder a um dado padrão e <em>apenas</em> aquele padrão.</p>
<p><a class="p_ident" id="p-7XOnvzIs0H" href="#p-7XOnvzIs0H" tabindex="-1" role="presentation"></a>Para cada um dos itens a seguir, escreva uma expressão regular para testar se o padrão dado ocorre em uma <em>string</em>. A expressão regular deve corresponder apenas a <em>strings</em> contendo o padrão. Quando sua expressão funcionar, veja se consegue torná-la menor.</p>
<ol>
<li>
<p><a class="p_ident" id="p-tlXRmuoHqc" href="#p-tlXRmuoHqc" tabindex="-1" role="presentation"></a><em>car</em> e <em>cat</em></p></li>
<li>
<p><a class="p_ident" id="p-ETQQ+3EAbu" href="#p-ETQQ+3EAbu" tabindex="-1" role="presentation"></a><em>pop</em> e <em>prop</em></p></li>
<li>
<p><a class="p_ident" id="p-2kqgaDH8ya" href="#p-2kqgaDH8ya" tabindex="-1" role="presentation"></a><em>ferret</em>, <em>ferry</em> e <em>ferrari</em></p></li>
<li>
<p><a class="p_ident" id="p-32nJx7pR3t" href="#p-32nJx7pR3t" tabindex="-1" role="presentation"></a>Qualquer palavra terminando em <em>ious</em></p></li>
<li>
<p><a class="p_ident" id="p-Gpk9OGbuMD" href="#p-Gpk9OGbuMD" tabindex="-1" role="presentation"></a>Um caractere de espaço em branco seguido de um ponto, vírgula, dois-pontos ou ponto e vírgula</p></li>
<li>
<p><a class="p_ident" id="p-P4H2QJYt69" href="#p-P4H2QJYt69" tabindex="-1" role="presentation"></a>Uma palavra com mais de seis letras</p></li>
<li>
<p><a class="p_ident" id="p-7kuKmeKs+z" href="#p-7kuKmeKs+z" tabindex="-1" role="presentation"></a>Uma palavra sem a letra <em>e</em> (ou <em>E</em>)</p></li>
</ol>
<p><a class="p_ident" id="p-avhQsJpOC5" href="#p-avhQsJpOC5" tabindex="-1" role="presentation"></a>Consulte a tabela no <a href="09_regexp.html#summary_regexp">resumo do capítulo</a> para ajuda. Teste cada solução com algumas <em>strings</em> de teste.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-jrxnp4Sj4d" href="#c-jrxnp4Sj4d" tabindex="-1" role="presentation"></a><span class="tok-comment">// Preencha as expressões regulares</span>
verify(<span class="tok-string2">/.../</span>,
[<span class="tok-string">"my car"</span>, <span class="tok-string">"bad cats"</span>],
[<span class="tok-string">"camper"</span>, <span class="tok-string">"high art"</span>]);
verify(<span class="tok-string2">/.../</span>,
[<span class="tok-string">"pop culture"</span>, <span class="tok-string">"mad props"</span>],
[<span class="tok-string">"plop"</span>, <span class="tok-string">"prrrop"</span>]);
verify(<span class="tok-string2">/.../</span>,
[<span class="tok-string">"ferret"</span>, <span class="tok-string">"ferry"</span>, <span class="tok-string">"ferrari"</span>],
[<span class="tok-string">"ferrum"</span>, <span class="tok-string">"transfer A"</span>]);
verify(<span class="tok-string2">/.../</span>,
[<span class="tok-string">"how delicious"</span>, <span class="tok-string">"spacious room"</span>],
[<span class="tok-string">"ruinous"</span>, <span class="tok-string">"consciousness"</span>]);
verify(<span class="tok-string2">/.../</span>,
[<span class="tok-string">"bad punctuation ."</span>],
[<span class="tok-string">"escape the period"</span>]);
verify(<span class="tok-string2">/.../</span>,
[<span class="tok-string">"Siebentausenddreihundertzweiundzwanzig"</span>],
[<span class="tok-string">"no"</span>, <span class="tok-string">"three small words"</span>]);
verify(<span class="tok-string2">/.../</span>,
[<span class="tok-string">"red platypus"</span>, <span class="tok-string">"wobbling nest"</span>],
[<span class="tok-string">"earth bed"</span>, <span class="tok-string">"bedrøvet abe"</span>, <span class="tok-string">"BEET"</span>]);
<span class="tok-keyword">function</span> <span class="tok-definition">verify</span>(<span class="tok-definition">regexp</span>, <span class="tok-definition">yes</span>, <span class="tok-definition">no</span>) {
<span class="tok-comment">// Ignorar exercícios inacabados</span>
<span class="tok-keyword">if</span> (regexp.source == <span class="tok-string">"..."</span>) <span class="tok-keyword">return</span>;
<span class="tok-keyword">for</span> (<span class="tok-keyword">let</span> <span class="tok-definition">str</span> <span class="tok-keyword">of</span> yes) <span class="tok-keyword">if</span> (!regexp.test(str)) {
console.log(<span class="tok-string2">`Failure to match '</span>${str}<span class="tok-string2">'`</span>);
}
<span class="tok-keyword">for</span> (<span class="tok-keyword">let</span> <span class="tok-definition">str</span> <span class="tok-keyword">of</span> no) <span class="tok-keyword">if</span> (regexp.test(str)) {
console.log(<span class="tok-string2">`Unexpected match for '</span>${str}<span class="tok-string2">'`</span>);
}
}</pre>
<h3><a class="i_ident" id="i-kRbN6N5BAE" href="#i-kRbN6N5BAE" tabindex="-1" role="presentation"></a>Estilo de citação</h3>
<p><a class="p_ident" id="p-Bjgagjg+7H" href="#p-Bjgagjg+7H" tabindex="-1" role="presentation"></a>Imagine que você escreveu uma história e usou aspas simples ao longo do texto para marcar trechos de diálogo. Agora quer substituir todas as aspas de diálogo por aspas duplas, mantendo as aspas simples usadas em contrações como <em>aren’t</em>.</p>
<p><a class="p_ident" id="p-fm8g2LRYEu" href="#p-fm8g2LRYEu" tabindex="-1" role="presentation"></a>Pense em um padrão que distinga esses dois tipos de uso de aspas e construa uma chamada ao método <code>replace</code> que faça a substituição adequada.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-Y+ie4WqTnM" href="#c-Y+ie4WqTnM" tabindex="-1" role="presentation"></a><span class="tok-keyword">let</span> <span class="tok-definition">text</span> = <span class="tok-string">"'I'm the cook,' he said, 'it's my job.'"</span>;
<span class="tok-comment">// Altere esta chamada.</span>
console.log(text.replace(<span class="tok-string2">/A/g</span>, <span class="tok-string">"B"</span>));
<span class="tok-comment">// → "I'm the cook," he said, "it's my job."</span></pre>
<details class="solution"><summary>Display hints...</summary><div class="solution-text">
<p><a class="p_ident" id="p-vHM3Ghp6+w" href="#p-vHM3Ghp6+w" tabindex="-1" role="presentation"></a>A solução mais óbvia é substituir apenas aspas com um caractere não-letra em pelo menos um lado — algo como <code>/\P{L}'|'\P{L}/u</code>. Mas você também precisa levar em conta o início e o final da linha.</p>
<p><a class="p_ident" id="p-wmsPu5AV2i" href="#p-wmsPu5AV2i" tabindex="-1" role="presentation"></a>Além disso, você deve garantir que a substituição também inclua os caracteres que foram correspondidos pelo padrão <code>\P{L}</code> para que não sejam descartados. Isso pode ser feito envolvendo-os em parênteses e incluindo seus grupos na <em>string</em> de substituição (<code>$1</code>, <code>$2</code>). Grupos que não foram correspondidos serão substituídos por nada.</p>
</div></details>
<h3><a class="i_ident" id="i-fog2+cmUFP" href="#i-fog2+cmUFP" tabindex="-1" role="presentation"></a>Números novamente</h3>
<p><a class="p_ident" id="p-W3zf7z5hQQ" href="#p-W3zf7z5hQQ" tabindex="-1" role="presentation"></a>Escreva uma expressão que corresponda apenas a números no estilo JavaScript. Ela deve suportar um sinal de menos <em>ou</em> mais opcional na frente do número, o ponto decimal e notação de expoente — <code>5e-3</code> ou <code>1E10</code> — novamente com um sinal opcional na frente do expoente. Também note que não é necessário haver dígitos antes ou depois do ponto, mas o número não pode ser apenas um ponto. Ou seja, <code>.5</code> e <code>5.</code> são números JavaScript válidos, mas um ponto sozinho não é.</p>
<pre tabindex="0" class="snippet" data-language="javascript" ><a class="c_ident" id="c-gwoI/msKY/" href="#c-gwoI/msKY/" tabindex="-1" role="presentation"></a><span class="tok-comment">// Preencha esta expressão regular.</span>
<span class="tok-keyword">let</span> <span class="tok-definition">number</span> = <span class="tok-string2">/^...$/</span>;
<span class="tok-comment">// Testes:</span>
<span class="tok-keyword">for</span> (<span class="tok-keyword">let</span> <span class="tok-definition">str</span> <span class="tok-keyword">of</span> [<span class="tok-string">"1"</span>, <span class="tok-string">"-1"</span>, <span class="tok-string">"+15"</span>, <span class="tok-string">"1.55"</span>, <span class="tok-string">".5"</span>, <span class="tok-string">"5."</span>,
<span class="tok-string">"1.3e2"</span>, <span class="tok-string">"1E-4"</span>, <span class="tok-string">"1e+12"</span>]) {
<span class="tok-keyword">if</span> (!number.test(str)) {
console.log(<span class="tok-string2">`Failed to match '</span>${str}<span class="tok-string2">'`</span>);
}
}
<span class="tok-keyword">for</span> (<span class="tok-keyword">let</span> <span class="tok-definition">str</span> <span class="tok-keyword">of</span> [<span class="tok-string">"1a"</span>, <span class="tok-string">"+-1"</span>, <span class="tok-string">"1.2.3"</span>, <span class="tok-string">"1+1"</span>, <span class="tok-string">"1e4.5"</span>,
<span class="tok-string">".5."</span>, <span class="tok-string">"1f5"</span>, <span class="tok-string">"."</span>]) {
<span class="tok-keyword">if</span> (number.test(str)) {
console.log(<span class="tok-string2">`Incorrectly accepted '</span>${str}<span class="tok-string2">'`</span>);
}
}</pre>
<details class="solution"><summary>Display hints...</summary><div class="solution-text">
<p><a class="p_ident" id="p-b70vEy4rbV" href="#p-b70vEy4rbV" tabindex="-1" role="presentation"></a>Primeiro, não esqueça a barra invertida antes do ponto.</p>
<p><a class="p_ident" id="p-oX9Qjf36I+" href="#p-oX9Qjf36I+" tabindex="-1" role="presentation"></a>Corresponder ao sinal opcional na frente do número, assim como na frente do expoente, pode ser feito com <code>[+\-]?</code> ou <code>(\+|-|)</code> (mais, menos ou nada).</p>
<p><a class="p_ident" id="p-Ef63HcA1cv" href="#p-Ef63HcA1cv" tabindex="-1" role="presentation"></a>A parte mais complicada do exercício é o problema de corresponder tanto <code>"5."</code> quanto <code>".5"</code> sem também corresponder a <code>"."</code>. Para isso, uma boa solução é usar o operador <code>|</code> para separar os dois casos — ou um ou mais dígitos opcionalmente seguidos de um ponto e zero ou mais dígitos <em>ou</em> um ponto seguido de um ou mais dígitos.</p>
<p><a class="p_ident" id="p-lbIZSUUGsb" href="#p-lbIZSUUGsb" tabindex="-1" role="presentation"></a>Finalmente, para tornar o <em>e</em> insensível a maiúsculas, adicione uma opção <code>i</code> à expressão regular ou use <code>[eE]</code>.</p>
</div></details><nav><a href="08_error.html" title="capítulo anterior" aria-label="capítulo anterior">◂</a> <a href="index.html" title="capa" aria-label="capa">●</a> <a href="10_modules.html" title="próximo capítulo" aria-label="próximo capítulo">▸</a> <button class=help title="ajuda" aria-label="ajuda"><strong>?</strong></button>
</nav>
</article>
<script src="ejs.js"></script>