-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy path09_regexp.html
More file actions
853 lines (539 loc) · 119 KB
/
09_regexp.html
File metadata and controls
853 lines (539 loc) · 119 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
<!doctype html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>عبارات باقاعده :: Eloquent JavaScript</title>
<link rel=stylesheet href="js/node_modules/codemirror/lib/codemirror.css">
<script src="js/acorn_codemirror.js"></script>
<link rel=stylesheet href="css/ejs.css">
<script src="js/sandbox.js"></script>
<script src="js/ejs.js"></script><script>var chapNum = 9;</script><script>var clicky_site_ids = clicky_site_ids || []; clicky_site_ids.push(101171577);</script>
<script async src="//static.getclicky.com/js"></script>
<script id='pixel-script-poptin' src='https://cdn.popt.in/pixel.js?id=ea6051b792008' async='true'></script>
</head>
<article>
<nav><a href="08_error.html" title="previous chapter">◀</a> <a href="index.html" title="cover">◆</a> <a href="10_modules.html" title="next chapter">▶</a></nav>
<h1><span class=chap_num>فصل 9</span>عبارات باقاعده</h1>
<blockquote>
<p><a class="p_ident" id="p_2jmj7l5rSw" href="#p_2jmj7l5rSw" tabindex="-1" role="presentation"></a>بعضیها، وقتی با مشکلی روبرو میشوند، فکر میکنند "خب، راه حل را میدانم، استفاده از عبارات باقاعده. " ولی حالا با دو مشکل روبرو هستند.</p>
<footer>جیمی زاوینسکی</footer>
</blockquote>
<blockquote>
<p>یوانما گفت، 'وقتی چوب را برخلاف جهت الیافش برش میدهید، نیروی بیشتری نیاز دارید. و هنگامی که بر خلاف روش صحیح حل مسئله، برنامهنویسی میکنید، به کد بیشتری نیاز دارید.’</p>
<footer>استاد یوانما, <cite>کتاب برنامهنویسی</cite></footer>
</blockquote><figure class="chapter square-framed"><img src="img/chapter_picture_9.jpg" alt="A railroad diagram"></figure>
<p>ابزارها و تکنیکهای برنامهنویسی در طول زمان به شکلی نامنظم و تکاملی حفظ میشوند و گسترش مییابند. اینطور نیست که همیشه آنهایی که درخشان یا خوب هستند برنده شوند؛ بلکه تکنیکها و ابزارهایی باقی میمانند که در یک حوزهی مناسب به اندازهی کافی خوب عمل میکنند یا این ویژگی را دارند که با تکنولوژی موفق دیگری به خوبی یکپارچه و تلفیق میشوند.</p>
<p>در این فصل، دربارهی یکی از این ابزارهای موفق، <em>عبارات باقاعده</em>، صحبت خواهم کرد. عبارات باقاعده روشی برای توصیف <em>الگوها</em> در دادههای متنی (رشتهای) میباشند. این عبارات، زبانی کوچک و مجزا را تشکیل میدهند که بخشی از زبان جاوااسکریپت و خیلی زبانها و سیستمهای دیگر محسوب میشوند.</p>
<p>عبارات باقاعده، به طور همزمان هم خیلی بیقواره و هم فوقالعاده کاربردی هستند. قواعد دستوری آنها رمزگونه و رابط برنامهنویسی آنها در جاوااسکریپت کمی نچسب است. اما ابزار بسیار قدرتمندی برای پردازش و وارسی رشتهها محسوب میشوند. درک صحیح عبارات باقاعده، شما را به برنامهنویس بهتری تبدیل میکند.</p>
<h2><a class="h_ident" id="h_QWP+tWudUY" href="#h_QWP+tWudUY" tabindex="-1" role="presentation"></a>ایجاد عبارات باقاعده</h2>
<p>یک عبارت باقاعده یک نوع شیء است. میتوان آن را هم با سازندهی <code>RegExp</code> و هم به طور مستقیم با قرار دادن یک الگو بین دو کاراکتر اسلش (<code>/</code>) ایجاد نمود.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_O1I2rl+HTy" href="#c_O1I2rl+HTy" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">re1</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">RegExp</span>(<span class="cm-string">"abc"</span>);
<span class="cm-keyword">let</span> <span class="cm-def">re2</span> <span class="cm-operator">=</span> <span class="cm-string-2">/abc/</span>;</pre>
<p><a class="p_ident" id="p_az8znGe7A9" href="#p_az8znGe7A9" tabindex="-1" role="presentation"></a>هر دوی عبارتهای باقاعدهی بالا نمایانگر یک الگو میباشند: کاراکتر <em>a</em> که بعد از آن <em>b</em> و بعد <em>c</em> می آید.</p>
<p>زمانی که از سازندهی <code>RegExp</code> استفاده میشود، الگو به صورت رشتهی معمولی نوشته میشود؛ بنابراین قوانین معمول برای کاراکتر بکاسلش برقرار است.</p>
<p>در روش دوم که در آن الگو بین دو کاراکتر اسلش ظاهر میشود، تفسیر بک اسلش کمی متفاوت است. اول اینکه، به دلیل اینکه کاراکتر اسلش نشان دهندهی پایان الگو است، باید یک بک اسلش را قبل از اسلشی که میخواهیم به عنوان بخشی از الگو تفسیر شود قرار دهیم. افزون بر آن، بک اسلشهایی که بخشی از کدکاراکترهای خاص (مانند <bdo><code>\n</code></bdo>) محسوب نمیشوند، بر خلاف حالت رشتهای، حفظ شده و باعث تغییر در معنای الگو خواهند شد. بعضی کاراکترها مثل علامت سوال یا مثبت، معانی خاصی در عبارات باقاعده دارند و اگر قرار است نمایانگر کاراکتر خودشان باشند، باید قبلشان یک بک اسلش قرار داده شود.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_uRzUiBSrul" href="#c_uRzUiBSrul" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">eighteenPlus</span> <span class="cm-operator">=</span> <span class="cm-string-2">/eighteen\+/</span>;</pre>
<h2><a class="h_ident" id="h_Ybl25j760a" href="#h_Ybl25j760a" tabindex="-1" role="presentation"></a>آزمایش تطبیق الگو</h2>
<p>اشیاء عبارات باقاعده دارای تعدادی متد میباشند. سادهترین آنها متد <code>test</code> است. اگر به این متد یک رشته ارسال کنید، با برگرداندن یک مقدار بولی، به شما خواهد گفت که آیا در رشتهی داده شده، نمونهای مطابق الگوی عبارت باقاعده، وجود دارد یا خیر.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Szn1CmrIV5" href="#c_Szn1CmrIV5" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/abc/</span>.<span class="cm-property">test</span>(<span class="cm-string">"abcde"</span>));
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/abc/</span>.<span class="cm-property">test</span>(<span class="cm-string">"abxde"</span>));
<span class="cm-comment">// → false</span></pre>
<p><a class="p_ident" id="p_qZk+NkcGgW" href="#p_qZk+NkcGgW" tabindex="-1" role="presentation"></a>اگر در عبارات باقاعده هیچ کاراکتر خاصی استفاده نشود، آن عبارت معادل همان دنبالهی کاراکترها میباشد. اگر <em>abc</em> در هر جای رشتهای که مورد آزمایش قرار دادهایم قرار گرفته باشد (نه فقط در شروع رشته)، متد <code>test</code> مقدار <code>true</code> را تولید میکند.</p>
<h2><a class="h_ident" id="h_y7YICXLgJL" href="#h_y7YICXLgJL" tabindex="-1" role="presentation"></a>مجموعههای کاراکتر</h2>
<p>فهمیدن اینکه آیا یک رشته حاوی <em>abc</em> هست یا خیر را میتوان به خوبی با متد <code>indexOf</code> نیز انجام داد. عبارات باقاعده به ما امکان تولید الگوهای پیچیدهتری را میدهند.</p>
<p>فرض کنید قصد داریم همه اعداد را شناسایی کنیم. در یک عبارت باقاعده، قرار دادن یک مجموعه کاراکتر درون براکت باعث میشود که آن بخش از عبارت با هر کاراکتری که بین براکتها آمده است تطبیق یابد.</p>
<p>هر دوی عبارتهای زیر همهی رشتههایی که دارای رقم هستند را شامل میشود:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Z3UJdL//cY" href="#c_Z3UJdL//cY" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/[0123456789]/</span>.<span class="cm-property">test</span>(<span class="cm-string">"in 1992"</span>));
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/[0-9]/</span>.<span class="cm-property">test</span>(<span class="cm-string">"in 1992"</span>));
<span class="cm-comment">// → true</span></pre>
<p><a class="p_ident" id="p_aHq8MjFa+g" href="#p_aHq8MjFa+g" tabindex="-1" role="presentation"></a>برای مشخص کردن یک بازه از کاراکترها میتوان درون براکتها از یک کاراکتر (<code>-</code>) بین دو کاراکتر استفاده کرد که ترتیب کاراکترها توسط کد یونیکد آنها مشخص میشود. کاراکترهای 0 تا 9 کنار هم و در بازهی یونیکد (کدهای 48 تا 57) قرار دارند بنابراین <bdo><code>[0-9]</code></bdo> همهی آنها را پوشش داده و هر رقمی را شامل میشود.</p>
<p>برای بعضی از گروههای کاراکتری روش کوتاهتری هم از پیش تعریف شده است. اعداد یکی از آنها هستند: مثلا <bdo><code>\d</code></bdo> معنایی مشابه <bdo><code>[0-9]</code></bdo> دارد.</p>
<table>
<tr><td><bdo><code>\d</code></bdo></td><td>هر کاراکتر عددی</td>
</tr>
<tr><td><bdo><code>\w</code></bdo></td><td>یک کاراکتر از نوع عدد یا حرف الفبا (“کاراکتر کلمه”)</td>
</tr>
<tr><td><bdo><code>\s</code></bdo></td><td>همهی کاراکترهای فضایخالی ( فاصله، تب، خط جدید، و مشابه آنها)</td>
</tr>
<tr><td><bdo><code>\D</code></bdo></td><td>کاراکتری که از نوع عدد <em>نباشد</em></td>
</tr>
<tr><td><bdo><code>\W</code></bdo></td><td>کاراکتری که عدد و حرف الفبا نباشد</td>
</tr>
<tr><td><bdo><code>\S</code></bdo></td><td>کاراکتری که فضای خالی محسوب نشود</td>
</tr>
<tr><td><code>.</code></td><td>همهی کاراکترها به جز کاراکتر خط جدید</td>
</tr>
</table>
<p><a class="p_ident" id="p_utaY+Pxc2d" href="#p_utaY+Pxc2d" tabindex="-1" role="presentation"></a>بنابراین میتوانید فرمت تاریخ و زمانی شبیه به <bdo>01-30-2003 15:20</bdo> را با عبارت زیر شناسایی کنید:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Y0e7M8nAL0" href="#c_Y0e7M8nAL0" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">dateTime</span> <span class="cm-operator">=</span> <span class="cm-string-2">/\d\d-\d\d-\d\d\d\d \d\d:\d\d/</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">dateTime</span>.<span class="cm-property">test</span>(<span class="cm-string">"01-30-2003 15:20"</span>));
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">dateTime</span>.<span class="cm-property">test</span>(<span class="cm-string">"30-jan-2003 15:20"</span>));
<span class="cm-comment">// → false</span></pre>
<p>ظاهر عبارت بالا خیلی بیقواره است، درست است؟ نیمی از آن بکاسلش است که الگو را بیش از حد شلوغ کرده و تشخیص معنای آن را سخت نموده است. در <a href="09_regexp.html#date_regexp_counted">ادامه</a> با نسخهای از آن که کمی بهبود یافته است آشنا خواهیم شد.</p>
<p>این کدهای بکاسلش را همچنین میتوان درون براکت استفاده کرد. به عنوان مثال، <bdo><code>[\d.]</code></bdo> به معنای یک رقم یا یک کاراکتر نقطه است. اما خود نقطه وقتی داخل براکت قرار میگیرد معنای خاصش را از دست میدهد. این قضیه برای دیگر کاراکترهای خاص مثل <code>+</code> هم برقرار است.</p>
<p>برای <em>معکوس</em> کردن یک مجموعهی کاراکتر – به این معنا که شما قصد دارید هر کاراکتری <em>بجز</em> آنهایی که در مجموعه مشخص شدهاند را بیان کنید – میتوانید از یک کاراکتر (<code>^</code>) بعد از براکت شروع بازه استفاده کنید.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_XH8deAcckk" href="#c_XH8deAcckk" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">notBinary</span> <span class="cm-operator">=</span> <span class="cm-string-2">/[^01]/</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">notBinary</span>.<span class="cm-property">test</span>(<span class="cm-string">"1100100010100110"</span>));
<span class="cm-comment">// → false</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">notBinary</span>.<span class="cm-property">test</span>(<span class="cm-string">"1100100010200110"</span>));
<span class="cm-comment">// → true</span></pre>
<h2><a class="h_ident" id="h_eRhbDjdfBc" href="#h_eRhbDjdfBc" tabindex="-1" role="presentation"></a>تکرار بخشهایی از یک الگو</h2>
<p>میدانیم که چگونه یک عدد یا رقم را شناسایی کنیم. چه باید کرد اگر بخواهیم که یک عدد کامل – دنبالهای از یک یا بیشتر رقم – را هدف قرار بدهیم؟</p>
<p>زمانی که یک علامت مثبت (<code>+</code>) را بعد از چیزی در یک عبارت باقاعده قرار میدهید، این علامت نشان میدهد که آن عنصر ممکن است یک بار یا بیشتر تکرار شود. بنابراین، <bdo> <code>/\d+/</code></bdo> به معنای مطابقت عبارت با تعداد یک یا بیشتر از کاراکترهای عددی خواهد بود.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_9/5mFF4Ih4" href="#c_9/5mFF4Ih4" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/'\d+'/</span>.<span class="cm-property">test</span>(<span class="cm-string">"'123'"</span>));
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/'\d+'/</span>.<span class="cm-property">test</span>(<span class="cm-string">"''"</span>));
<span class="cm-comment">// → false</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/'\d*'/</span>.<span class="cm-property">test</span>(<span class="cm-string">"'123'"</span>));
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/'\d*'/</span>.<span class="cm-property">test</span>(<span class="cm-string">"''"</span>));
<span class="cm-comment">// → true</span></pre>
<p>کاراکتر ستاره (<code>*</code>) معنای مشابهی دارد با این تفاوت که به الگو اجازه میدهد تا صفر بار تکرار (نبودن کاراکتر) را هم شامل شود. اگر بعد از چیزی کاراکتر ستاره قرار گیرد، باعث میشود که الگو همیشه چیزی برای مطابقت پیدا کند – در صورتی که نتواند متنی برای مطابقت پیدا کند، با نبود آن عنصر مطابقت خواهد داد.</p>
<p><a class="p_ident" id="p_UeaYkqtJ34" href="#p_UeaYkqtJ34" tabindex="-1" role="presentation"></a>استفاده از علامت سوال (?) در یک الگو به معنای <em>اختیاری</em> بودن است، یعنی ممکن است که آن عنصر نباشد یا یک بار حاضر باشد. در مثال پیش رو، کاراکتر <em>u</em> اختیاری است و میتواند باشد و در صورت نبودن هم الگو صدق خواهد کرد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_EiCIowdq+d" href="#c_EiCIowdq+d" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">neighbor</span> <span class="cm-operator">=</span> <span class="cm-string-2">/neighbou?r/</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">neighbor</span>.<span class="cm-property">test</span>(<span class="cm-string">"neighbour"</span>));
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">neighbor</span>.<span class="cm-property">test</span>(<span class="cm-string">"neighbor"</span>));
<span class="cm-comment">// → true</span></pre>
<p><a class="p_ident" id="p_G2RTiSRzpG" href="#p_G2RTiSRzpG" tabindex="-1" role="presentation"></a>برای مشخص کردن این موضوع که یک الگو باید به تعداد دقیقی رخ دهد، میتوانید از کروشه استفاده کنید؛ به عنوان مثال، قرار دادن <code>{4}</code> بعد از یک عنصر، باعث میشود که الگو انتظار داشته باشد آن عنصر دقیقا 4 مرتبه رخ داده باشد. همچنین میتوان یک بازه را نیز مشخص نمود: <bdo><code>{2,4}</code></bdo> به این معنا است که این عنصر باید حداقل دو مرتبه و حداکثر چهار مرتبه رخ دهد.</p>
<p id="date_regexp_counted">اینجا نسخهی دیگر از الگوی تشخیص تاریخ و زمان را داریم که امکان تشخیص روز، ماه و ساعت به هر دو فرمت تک رقمی و دو رقمی را دارد. همچنین درک این الگو کمی راحتتر از الگوی پیشین است.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Tw+K6Mxe45" href="#c_Tw+K6Mxe45" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">dateTime</span> <span class="cm-operator">=</span> <span class="cm-string-2">/\d{1,2}-\d{1,2}-\d{4} \d{1,2}:\d{2}/</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">dateTime</span>.<span class="cm-property">test</span>(<span class="cm-string">"1-30-2003 8:45"</span>));
<span class="cm-comment">// → true</span></pre>
<p>همچنین میتوانید بازههایی که انتهایی باز دارند را نیز مشخص کنید. این کار با حذف رقم پس از ویرگول انجام میشود. بنابراین، <bdo><code>{5,}</code></bdo> به معنای پنج یا بیشتر میباشد.</p>
<h2><a class="h_ident" id="h_FixWcalhE0" href="#h_FixWcalhE0" tabindex="-1" role="presentation"></a>دستهبندی زیرعبارات</h2>
<p>برای استفاده از یک عملگر مانند <code>*</code> یا <code>+</code> روی بیش از یک عنصر در آنِ واحد، باید از پرانتز استفاده کنید. از دید عملگرهایی که بعد از عبارتهای داخل پرانتز قرار میگیرند، هر عبارت محصور بین پرانتز به عنوان یک عنصر در نظر گرفته میشود.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_P/f6a65XwI" href="#c_P/f6a65XwI" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">cartoonCrying</span> <span class="cm-operator">=</span> <span class="cm-string-2">/boo+(hoo+)+/i</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">cartoonCrying</span>.<span class="cm-property">test</span>(<span class="cm-string">"Boohoooohoohooo"</span>));
<span class="cm-comment">// → true</span></pre>
<p><a class="p_ident" id="p_WI3aP26c24" href="#p_WI3aP26c24" tabindex="-1" role="presentation"></a>کاراکترهای <code>+</code> اول و دوم فقط به <em>o</em> دوم از <em>boo</em> و <em>hoo</em> اعمال میشوند. کاراکتر + سوم به کل گروه <bdo><code>(hoo+)</code></bdo> اعمال میشود و یک یا بیش از یک بار تکرار آن الگو را شامل میشود.</p>
<p><a class="p_ident" id="p_rk8oHfWl0P" href="#p_rk8oHfWl0P" tabindex="-1" role="presentation"></a>کاراکتر <code>i</code> که در انتهای عبارت مثال آمده است باعث میشود که عبارت باقاعده به بزرگی و کوچکی حروف حساس نباشد، یعنی کاراکتر <em>B</em> بزرگ هم در رشتهی ورودی تطبیق خواهد خورد، با وجود اینکه الگو خودش به حروف کوچک نوشته شده است.</p>
<h2><a class="h_ident" id="h_XX70toH0eo" href="#h_XX70toH0eo" tabindex="-1" role="presentation"></a>تطبیقها و گروهها</h2>
<p>متد <code>test</code> سادهترین راهی است که برای تطبیق یک عبارت باقاعده استفاده میشود. این متد فقط تطبیق و عدم تطبیق عبارت را مشخص میکند و دیگر هیچ. عبارات باقاعده همچنین متدی به نام <code>exec</code> (به معنای اجرا) دارند که در صورت نبود تطبیق، مقدار <code>null</code> را برمی گرداند و در صورت وجود تطبیق، شیئی شامل اطلاعاتی راجع به آن تولید میکند.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_JJMWZpk0iD" href="#c_JJMWZpk0iD" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">match</span> <span class="cm-operator">=</span> <span class="cm-string-2">/\d+/</span>.<span class="cm-property">exec</span>(<span class="cm-string">"one two 100"</span>);
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">match</span>);
<span class="cm-comment">// → ["100"]</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">match</span>.<span class="cm-property">index</span>);
<span class="cm-comment">// → 8</span></pre>
<p>شیئی که از یک متد <code>exec</code> برگردانده میشود خاصیتی به نام <code>index</code> دارد که نقطه شروع تطبیق پیدا شده را در رشته به ما مینشان میدهد. علاوه بر آن، این شیء شبیه به (و در واقع یک) آرایهای از رشتهها است، که عنصر اولش رشتهای است که با الگو مطابقت داشته است – در مثال قبل، دنبالهای از اعداد که به دنبال آن بودیم.</p>
<p>مقدارهای رشتهای متدی به نام <code>match</code> دارند که به شکل مشابهی عمل میکند.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_uAkAqNYx+q" href="#c_uAkAqNYx+q" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"one two 100"</span>.<span class="cm-property">match</span>(<span class="cm-string-2">/\d+/</span>));
<span class="cm-comment">// → ["100"]</span></pre>
<p>زمانی که یک عبارت باقاعده شامل زیرعبارتهایی باشد که با پرانتز گروهبندی شدهاند، متنهایی که با آن گروهها مطابقت دارند نیز درون یک آرایه نمایش داده خواهد شد. تطبیق کامل همیشه در همان عنصر اول است. عنصر بعدی آرایه متعلق به بخشی است که توسط اولین گروه تطبیق یافته است (گروهی که پرانتز شروعش در عبارت اول آمده است)، سپس گروه دوم و الی آخر.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_5E2M1BBsUm" href="#c_5E2M1BBsUm" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">quotedText</span> <span class="cm-operator">=</span> <span class="cm-string-2">/'([^']*)'/</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">quotedText</span>.<span class="cm-property">exec</span>(<span class="cm-string">"she said 'hello'"</span>));
<span class="cm-comment">// → ["'hello'", "hello"]</span></pre>
<p>زمانی که برای یک گروه تطبیقی در رشته پیدا نمیشود (به عنوان مثال، زمانی که بعد از گروه علامت سوال قرار گرفته باشد) موقعیت آن در آرایهی خروجی به صورت <code>undefined</code> خواهد بود. به طور مشابه، اگر یک گروه چندین تطبیق داشته باشد، فقط آخرین آنها در آرایه قرار خواهد گرفت.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_j9t+gn+1eT" href="#c_j9t+gn+1eT" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/bad(ly)?/</span>.<span class="cm-property">exec</span>(<span class="cm-string">"bad"</span>));
<span class="cm-comment">// → ["bad", undefined]</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/(\d)+/</span>.<span class="cm-property">exec</span>(<span class="cm-string">"123"</span>));
<span class="cm-comment">// → ["123", "3"]</span></pre>
<p>از قابلیت گروهها میتوان برای استخراج قسمتهای یک رشته استفاده کرد. به عنوان مثال، زمانی که فقط بودن یک تاریخ در یک رشته برای ما مهم نیست و قصد داریم تا آن را از دل آن استخراج کرده و شیئی حاوی آن بسازیم، میتوانیم با استفاده از پرانتز در الگوی ارقام، به طور مستقیم آن را در نتیجهی <code>exec</code> مجزا کنیم.</p>
<p>اما ابتدا، یک فاصلهی کوتاه بگیریم و کمی در رابطه با روش از پیش تعریف شده برای نمایش مقادیر زمان و تاریخ در جاوااسکریپت صحبت کنیم.</p>
<h2><a class="h_ident" id="h_blRb/5zgjW" href="#h_blRb/5zgjW" tabindex="-1" role="presentation"></a>کلاس Date</h2>
<p>جاوااسکریپت کلاس استانداردی برای نمایش تاریخها – یا به عبارتی نقاطی در زمان – دارد. این کلاس <code>Date</code> نامیده میشود. اگر با <code>new</code> یک کلاس تاریخ ایجاد کنید، زمان و تاریخ فعلی را خواهید گرفت.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_AjgqFetryg" href="#c_AjgqFetryg" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Date</span>());
<span class="cm-comment">// → Mon Nov 13 2017 16:19:11 GMT+0100 (CET)</span></pre>
<p>همچنین میتوانید یک شیء برای یک تاریخ مشخص ایجاد کنید.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_2VCU0f4HsQ" href="#c_2VCU0f4HsQ" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Date</span>(<span class="cm-number">2009</span>, <span class="cm-number">11</span>, <span class="cm-number">9</span>));
<span class="cm-comment">// → Wed Dec 09 2009 00:00:00 GMT+0100 (CET)</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Date</span>(<span class="cm-number">2009</span>, <span class="cm-number">11</span>, <span class="cm-number">9</span>, <span class="cm-number">12</span>, <span class="cm-number">59</span>, <span class="cm-number">59</span>, <span class="cm-number">999</span>));
<span class="cm-comment">// → Wed Dec 09 2009 12:59:59 GMT+0100 (CET)</span></pre>
<p><a class="p_ident" id="p_F7oHkUmduQ" href="#p_F7oHkUmduQ" tabindex="-1" role="presentation"></a>جاوااسکریپت از قراردادی استفاده میکند که در آن ماهها از صفر شروع میشوند (بنابراین ماه دسامبر برابر 11 خواهد شد)، اما روزها از یک شروع میشوند. این به نظر گیجکننده و احمقانه میرسد. پس دقت داشته باشید.</p>
<p><a class="p_ident" id="p_78ZbbZFvoy" href="#p_78ZbbZFvoy" tabindex="-1" role="presentation"></a>چهار آرگومان آخر (hours، minutes، seconds و milliseconds) اختیاری هستند و اگر مشخص نشوند با صفر مقداردهی میشوند.</p>
<p><a class="p_ident" id="p_1/gBKgrdK7" href="#p_1/gBKgrdK7" tabindex="-1" role="presentation"></a>برچسبهای ثبت زمان (timestamp) به عنوان تعداد هزارم ثانیههایی ذخیره میشوند که از شروع سال 1970 میلادی در ناحیه زمانی UTC میگذرد. این روش بر اساس “Unix time” است که خود حدود همان سال اختراع شد. میتوانید برای زمانهای قبل از 1970 از اعداد منفی استفاده کنید. متد <code>getTime</code> روی یک شیء Date این عدد را تولید میکند. این عدد همانطور که میتوانید حدس بزنید رقم بزرگی است.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_lMlCuckMIc" href="#c_lMlCuckMIc" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Date</span>(<span class="cm-number">2013</span>, <span class="cm-number">11</span>, <span class="cm-number">19</span>).<span class="cm-property">getTime</span>());
<span class="cm-comment">// → 1387407600000</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Date</span>(<span class="cm-number">1387407600000</span>));
<span class="cm-comment">// → Thu Dec 19 2013 00:00:00 GMT+0100 (CET)</span></pre>
<p>اگر به تابع سازندهی <code>Date</code> یک آرگومان ارسال نمایید، این آرگومان به عنوان همان شمارش هزارم ثانیهها تفسیر میشود. میتوانید تعداد هزام ثانیههای لحظهی کنونی را با ایجاد یک شیء جدید <code>Date</code> و فراخوانی متد <code>getTime</code> روی آن یا با فراخوانی تابع <bdo><code>Date.now</code></bdo> بدست بیاورید.</p>
<p><a class="p_ident" id="p_uWWNdbwVF7" href="#p_uWWNdbwVF7" tabindex="-1" role="presentation"></a>اشیاء Date متدهایی مانند <code>getFullYear</code>، <code>getMonth</code>، <code>getDate</code>، <code>getHours</code>، <code>getMinutes،</code> و <code>getSeconds</code> را فراهم میکنند که بتوان اجزای یک تاریخ را به وسیلهی آنها استخراج کرد. در کنار متد <code>getFullYear</code>، متدی به نام <code>getYear</code> نیز وجود دارد، که سال را با کسر از 1900 تولید میکند (مثل <code>98</code> یا <code>119</code>) که تقریبا کاربردی ندارد.</p>
<p>با قراردادن پرانتز دور بخشهای عبارتی که به آن نیاز داریم، میتوانیم شیء تاریخ را از یک رشته ایجاد کنیم.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_xW0xfMnpiZ" href="#c_xW0xfMnpiZ" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">getDate</span>(<span class="cm-def">string</span>) {
<span class="cm-keyword">let</span> [<span class="cm-def">_</span>, <span class="cm-def">month</span>, <span class="cm-def">day</span>, <span class="cm-def">year</span>] <span class="cm-operator">=</span>
<span class="cm-string-2">/(\d{1,2})-(\d{1,2})-(\d{4})/</span>.<span class="cm-property">exec</span>(<span class="cm-variable-2">string</span>);
<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">Date</span>(<span class="cm-variable-2">year</span>, <span class="cm-variable-2">month</span> <span class="cm-operator">-</span> <span class="cm-number">1</span>, <span class="cm-variable-2">day</span>);
}
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">getDate</span>(<span class="cm-string">"1-30-2003"</span>));
<span class="cm-comment">// → Thu Jan 30 2003 00:00:00 GMT+0100 (CET)</span></pre>
<p>کاراکتر خط زیرین (<code>_</code>) که در مثال به عنوان یک متغیر استفاده شده است، در اینجا استفادهای ندارد و فقط برای عبور از خانهی اول آرایهی تولیدی <code>exec</code> استفاده شده است.</p>
<h2><a class="h_ident" id="h_ZXV7mPWekf" href="#h_ZXV7mPWekf" tabindex="-1" role="presentation"></a>مرزهای واژه و رشته</h2>
<p><a class="p_ident" id="p_P7OhqghsID" href="#p_P7OhqghsID" tabindex="-1" role="presentation"></a>متاسفانه، متد <code>getDate</code> همچنین تاریخهای غلطی مانند <bdo>00-1-3000</bdo> را از رشتهی <bdo><code>"100-1-30000"</code></bdo> استخراج میکند. یک تطبیق ممکن است در هرجای رشته رخ بدهد، بنابراین در این مورد، از کاراکتر دوم این رشته شروع میشود و در کاراکتر یکی مانده به پایان، تمام میشود.</p>
<p><a class="p_ident" id="p_EfatjsUqKY" href="#p_EfatjsUqKY" tabindex="-1" role="presentation"></a>اگر بخواهیم تطبیق شامل کل رشته باشد، باید با استفاده از نشانگرهای <code>^</code> و <code>$</code> این کار را انجام دهیم. کاراکتر <code>^</code>، شروع رشتهی ورودی را مشخص میکند، در حالیکه کاراکتر <code>$</code>، این کار را برای پایان انجام میدهد. بنابراین <bdo><code>/^\d+$/</code></bdo> رشتهای را تطبیق خواهد داد که کلا دارای یک یا بیش از یک رقم باشد، <bdo><code>/^!/</code></bdo> شامل همهی رشتههایی میشود که با یک علامت تعجب شروع شده باشند، و <bdo><code>/x^/</code></bdo> هیچ رشتهای را شامل نخواهد شد (نمیتوان یک کاراکتر <em>x</em> را قبل از کاراکتر شروع یک رشته تصور کرد).</p>
<p>اگر، از سوی دیگر، بخواهیم مطمئن شویم که تاریخ مورد نظر در مرزهای یک کلمه شروع و پایان مییابد، میتوانیم از نشانگر <bdo><code>\b</code></bdo> استفاده کنیم. یک مرز کلمه میتواند شروع یا پایان یک رشته یا هر نقطهای در رشته باشد که یک کارکتر از نوع کلمه (حرف الفبا یا رقم مثل <bdo><code>\w</code></bdo>) در یک سمت داشته باشد و یک کاراکتر غیرکلمهای در سمت دیگر داشته باشد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_6U0b866tUk" href="#c_6U0b866tUk" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/cat/</span>.<span class="cm-property">test</span>(<span class="cm-string">"concatenate"</span>));
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/\bcat\b/</span>.<span class="cm-property">test</span>(<span class="cm-string">"concatenate"</span>));
<span class="cm-comment">// → false</span></pre>
<p>توجه داشته باشید که یک نشانگر تعیین مرز (حدود) خود کاراکتری را تطبیق نمیدهد. این نشانگر فقط باعث میشود که عبارت باقاعده فقط زمانی تطبیق بخورد که یک شرط مشخص در نقطهای که نشانگر در الگو قرار گرفته برقرار باشد.</p>
<h2><a class="h_ident" id="h_WidEO7HCmM" href="#h_WidEO7HCmM" tabindex="-1" role="presentation"></a>الگوهای انتخاب</h2>
<p><a class="p_ident" id="p_RPL9YC3XKT" href="#p_RPL9YC3XKT" tabindex="-1" role="presentation"></a>فرض کنید بخواهیم بدانیم که در یک رشتهی متنی عددی وجود دارد که بعد از آن یکی از کلمههای <em>pig</em>، <em>cow</em>، یا <em>chicken</em> به صورت مفرد یا جمع آمده باشد.</p>
<p>میتوانیم سه عبارت باقاعدهی مجزا نوشته و هر کدام را به نوبت روی نوشته آزمایش کنیم. اما یک راه بهتر نیز وجود دارد. کاراکتر پایپ (|) امکان انتخاب بین الگوی سمت راست و چپش را فراهم میکند. بنابراین میتوانیم بنویسیم:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_z0soEIN8RB" href="#c_z0soEIN8RB" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">animalCount</span> <span class="cm-operator">=</span> <span class="cm-string-2">/\b\d+ (pig|cow|chicken)s?\b/</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">animalCount</span>.<span class="cm-property">test</span>(<span class="cm-string">"15 pigs"</span>));
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">animalCount</span>.<span class="cm-property">test</span>(<span class="cm-string">"15 pigchickens"</span>));
<span class="cm-comment">// → false</span></pre>
<p>میتوان با استفاده از پرانتز بخشهایی از الگو که عملگر پایپ روی آنها اعمال میشود را محدود کرد، و نیز میتوان چندین عملگر پایپ را کنار هم قرار داد تا امکان انتخاب بین بیش از دو جایگزین را فراهم نمود.</p>
<h2><a class="h_ident" id="h_+IQq5RjxIb" href="#h_+IQq5RjxIb" tabindex="-1" role="presentation"></a>مکانیزم تطبیقدهی</h2>
<p>از نظر مفهومی، زمانی که از متد <code>exec</code> یا <code>test</code> استفاده میکنید، موتور عبارت باقاعده به دنبال تطبیقی در رشتهی شما میگردد و سعی دارد این کار را با تطبیق دادن عبارت از ابتدای رشته انجام دهد، سپس از کاراکتر دوم، و همین طور ادامه میدهد تا اینکه تطبیقی پیدا کند یا به انتهای رشتهی داده شده برسد. در پایان رشته، یا اولین تطبیق ممکن را برمیگرداند یا جستجو با شکست روبرو میشود.</p>
<p>موتور جاوااسکریپت برای انجام تطبیق، با عبارت باقاعده مانند یک نمودار جریان برخورد میکند. نمودار پایین برای عبارت مربوط به مثال حیوانات است:</p><figure><img src="img/re_pigchickens.svg" alt="Visualization of /\b\d+ (pig|cow|chicken)s?\b/"></figure>
<p>عبارت ما موفق به تطبیق خواهد شد اگر بتوانیم مسیری از سمت چپ نمودار به سمت راست آن بیابیم. موقعیت فعلی را در رشته حفظ میکنیم، و هر بار که به سمت یک مستطیل حرکت میکنیم، مطمئن میشویم که بخشی از رشته که بعد از موقعیت فعلی ما قرار دارد با آن مستطیل تطبیق دارد.</p>
<p>بنابراین اگر سعی کنیم که رشتهی <code>"the 3 pigs"</code> را از موقعیت 4 تطبیق دهیم، پیشروی ما در نمودار چیزی شبیه به زیر میشود:</p>
<ul>
<li>
<p>در موقعیت 4، یک مرز واژه وجود دارد، پس باید از اولین مستطیل عبور کنیم.</p></li>
<li>
<p>هنوز در موقعیت 4 هستیم، یک عدد میبینیم، پس میتوان از مستطیل بعدی نیز عبور کرد.</p></li>
<li>
<p><a class="p_ident" id="p_rDR41po8gf" href="#p_rDR41po8gf" tabindex="-1" role="presentation"></a>در موقعیت 5، یک مسیر به مستطیل دوم (رقم) بر میگردد، در حالیکه مسیر دیگر به سمت مستطیلی میرود که یک کاراکتر فضای خالی را نگه میدارد. در اینجا یک فضای خالی وجود دارد، نه یک رقم، پس باید از مسیر دوم برویم.</p></li>
<li>
<p><a class="p_ident" id="p_C2QVX7oEpp" href="#p_C2QVX7oEpp" tabindex="-1" role="presentation"></a>اکنون در موقعیت 6 (شروع رشتهی pigs) قرار داریم و در شاخهی سهراهی نمودار. <em>cow</em> و <em>chiken</em> را اینجا نمیبینیم اما <em>pig</em> را میبینیم پس به سراغ آن شاخه میرویم.</p></li>
<li>
<p><a class="p_ident" id="p_2BFtCV9RI8" href="#p_2BFtCV9RI8" tabindex="-1" role="presentation"></a>در موقعیت 9، بعد از شاخهی سه راهی، یک مسیر مستطیل <em>s</em> را نادیده میگیرد و مستقیما به مرز واژهی نهایی میرود، درحالیکه مسیر دیگر یک <em>s</em> را تطبیق میدهد. در اینجا ما یک کاراکتر <em>s</em> داریم نه یک مرز کلمه، پس به سراغ مستطیل <em>s</em> میرویم.</p></li>
<li>
<p><a class="p_ident" id="p_sdV4ERHYT3" href="#p_sdV4ERHYT3" tabindex="-1" role="presentation"></a>در موقعیت 10 (پایان رشته) قرار گرفتهایم و تنها میتوانیم یک مرز کلمه را تطبیق دهیم. پایان رشته به معنای یک مرز کلمه است؛ پس به سراغ آخرین مستطیل میرویم و با موفقیت این رشته را تطبیق میدهیم.</p></li></ul>
<h2 id="backtracking"><a class="h_ident" id="h_QAgvSTG0xW" href="#h_QAgvSTG0xW" tabindex="-1" role="presentation"></a>عقبگرد</h2>
<p><a class="p_ident" id="p_ac6u+t1n2x" href="#p_ac6u+t1n2x" tabindex="-1" role="presentation"></a>عبارت باقاعدهی <bdo><code>/<wbr>\b([01]+b|[\da-f]+h|\d+)\b/<wbr></code></bdo> یکی از اعداد زیر را تطبیق میدهد: یک عدد دودویی که بعد از آن یک b آمده باشد، یک عدد هگزادسیمال (عددی در مبنای 16 که دارای حروف <em>a</em> تا <em>f</em> است که برای اعداد 10 تا 15 استفاده میشوند) که بعد از آن یک <em>h</em> قرار گرفته، یا یک عدد دهدهی معمولی که هیچ پسوندی ندارد. نمودار زیر مربوط به این عبارت است:</p><figure><img src="img/re_number.svg" alt="Visualization of /\b([01]+b|\d+|[\da-f]+h)\b/"></figure>
<p><a class="p_ident" id="p_d95o2uzYI7" href="#p_d95o2uzYI7" tabindex="-1" role="presentation"></a>در زمان تطبیق این عبارت، اغلب اینگونه میشود که علی رغم اینکه ممکن است ورودی دارای عدد دودویی نباشد، اما شاخهی بالایی (دودویی) انتخاب میشود. در زمان تطبیق رشتهی <code>"103"</code> به عنوان مثال، فقط زمانی متوجه میشویم که در شاخهی اشتباهی قرار داریم که به کاراکتر 3 برسیم. رشته با عبارت تطبیق دارد اما نه لزوما با شاخهای که در حال حاضر در آن قرار گرفتهایم.</p>
<p><a class="p_ident" id="p_QTFUBgLx6+" href="#p_QTFUBgLx6+" tabindex="-1" role="presentation"></a>بنابراین تطبیقدهنده عقبگرد انجام میدهد. هنگام ورود به یک شاخه، موقعیت کنونی خودش را به خاطر میسپارد (در اینجا، در ابتدای رشته، درست قبل از اولین مستطیل مرز (محدوده) در نمودار) با این کار میتواند به عقب برگردد و اگر شاخهی فعلی جواب نداد به سراغ شاخهی دیگری برود. برای رشتهی <code>"103"</code> بعد از مواجه با کاراکتر 3، به سراغ شاخهی اعداد هگزادسیمال میرود، که نتیجهای نخواهد داشت به این دلیل که بعد از عدد، هیج کاراکتر <em>h</em> ای وجود ندارد. بنابراین به سراغ شاخهی عدد دهدهی میرود. این شاخه انتخاب درستی است و یک تطبیق در پایان گزارش داده میشود.</p>
<p>تطبیقگر به محض اینکه یک تطبیق کامل پیدا میکند متوقف میشود. معنای این کار این است که اگر چندین شاخهی بالقوه برای تطبیق یک رشته موجود باشد، فقط اولین شاخه (به ترتیبی که شاخه در عبارت منظم قرار گرفته است) استفاده میشود.</p>
<p><a class="p_ident" id="p_VuhgpgUJq6" href="#p_VuhgpgUJq6" tabindex="-1" role="presentation"></a>عقبگرد همچنین برای عملگرهای تکرار مثل <code>+</code> و <code>*</code> نیز اتفاق می افتد. اگر الگوی <bdo><code>/^.*x/</code></bdo> را روی رشتهی <code>"abcxe"</code> تطبیق دهید، قسمت <bdo><code>.*</code></bdo>، ابتدا سعی میکند که تمام رشته را مصرف کند. موتور سپس متوجه میشود که نیاز به یک <em>x</em> دارد تا بتواند الگو را تطبیق دهد. چون هیچ <em>x</em> ای قبل از پایان رشته وجود ندارد، عملگر <code>*</code> سعی میکند تا یک کاراکتر کمتر را تطبیق دهد. اما تطبیقگر، <em>x</em> را بعد از <code>abcx</code> نیز پیدا نمیکند بنابراین عقبگرد دوباره اتفاق میافتد که موجب میشود عملگر ستاره فقط <code>abc</code> را تطبیق دهد. <em>اکنون</em> یک <em>x</em> درست جایی که لازمش دارد پیدا میکند و آن را به عنوان یک تطبیق موفق از موقعیت 0 تا 4 گزارش میدهد.</p>
<p>میتوان عبارات باقاعدهای نوشت که در آنها تعداد <em>زیادی</em> عقبگرد انجام شود. این مشکل زمانی رخ میدهد که یک الگو میتواند یک ورودی را به شیوههای زیاد و متفاوتی تطبیق دهد. به عنوان مثال، اگر هنگام نوشتن یک عبارت باقاعده برای یک عدد دودویی حواسمان نباشد، ممکن است تصادفا چیزی شبیه <bdo><code>/([01]+)+b/</code></bdo> بنویسیم.</p><figure><img src="img/re_slow.svg" alt="Visualization of /([01]+)+b/"></figure>
<p><a class="p_ident" id="p_dCURq8BxyO" href="#p_dCURq8BxyO" tabindex="-1" role="presentation"></a>اگر این الگو سعی کند که سریهای بلندی از صفر و یکها را بدون کاراکتر پایانی <em>b</em> تطبیق دهد، تطبیقگر ابتدا سراغ حلقهی درونی میرود تا اینکه تمامی اعداد تمام شوند. سپس متوجه میشود که کاراکتر <em>b</em> وجود ندارد، بنابراین یک مکان (موقعیت) عقبگرد میکند، یک بار به سراغ حلقهی بیرونی میرود و نتیجهای نمیگیرد، دوباره برای خروج از حلقهی درونی عقبگرد انجام میدهد. یعنی مقدار کار انجام شده به ازای هر کاراکتر دو برابر میشود. حتی برای چند دوجین کاراکتر، عمل تطبیق در واقع برای همیشه طول خواهد کشید.</p>
<h2><a class="h_ident" id="h_ZLM3tPslu1" href="#h_ZLM3tPslu1" tabindex="-1" role="presentation"></a>متد replace</h2>
<p>مقادیر رشتهای دارای متدی به نام <code>replace</code> هستند که میتوان از آن برای جایگزینی بخشی از رشته با رشتهای دیگر استفاده کرد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_dPdIdK/Wyi" href="#c_dPdIdK/Wyi" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"papa"</span>.<span class="cm-property">replace</span>(<span class="cm-string">"p"</span>, <span class="cm-string">"m"</span>));
<span class="cm-comment">// → mapa</span></pre>
<p>آرگومان اول این متد همچنین میتواند یک عبارت باقاعده باشد، که در این صورت، اولین تطبیق پیدا شده توسط عبارت باقاعده، با رشتهی مورد نظر جایگزین میشود. زمانی که گزینهی <code>g</code> (سراسری) به عبارت باقاعده اضافه شود، به جای جایگزینی اولین مورد، تمامی تطبیقهای پیداشده در رشته، جایگزین خواهند شد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_ztGnSKyKy1" href="#c_ztGnSKyKy1" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Borobudur"</span>.<span class="cm-property">replace</span>(<span class="cm-string-2">/[ou]/</span>, <span class="cm-string">"a"</span>));
<span class="cm-comment">// → Barobudur</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Borobudur"</span>.<span class="cm-property">replace</span>(<span class="cm-string-2">/[ou]/g</span>, <span class="cm-string">"a"</span>));
<span class="cm-comment">// → Barabadar</span></pre>
<p>بهتر به نظر میرسید اگر گزینهی انتخاب بین جایگزینی همهی تطبیقها یا یک تطبیق، به شکل یک آرگومان مجزا برای متد <code>replace</code> تعریف میشد یا اینکه متدی متفاوت برای آن در نظر گرفته میشد؛ مانند <code>replaceAll</code>. اما از بد روزگار، این گزینه وابسته به خاصیتی در عبارت باقاعده میباشد.</p>
<p>قدرت اصلی استفاده از عبارات باقاعده به وسیلهی متد <code>replace</code> اینجا است که میتوانیم به گروههای تطبیق خورده در رشتهی جایگزین رجوع کنیم. به عنوان مثال، فرض کنید که یک رشتهی بزرگ که حاوی نام افراد است در اختیار داریم، در هر خط یک نام وجود دارد و فرمت آن به شکل <bdo><code>Lastname, Firstname</code></bdo> میباشد. اگر بخواهیم ترتیب قرارگیری نامها را عوض کرده و ویرگول بین آن را حذف کنیم، میتوانیم از کد زیر استفاده کنیم:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_5P5aZAbVLL" href="#c_5P5aZAbVLL" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(
<span class="cm-string">"Liskov, Barbara\nMcCarthy, John\nWadler, Philip"</span>
.<span class="cm-property">replace</span>(<span class="cm-string-2">/(\w+), (\w+)/g</span>, <span class="cm-string">"$2 $1"</span>));
<span class="cm-comment">// → Barbara Liskov</span>
<span class="cm-comment">// John McCarthy</span>
<span class="cm-comment">// Philip Wadler</span></pre>
<p><bdo><code>$1</code></bdo> و <bdo><code>$2</code></bdo> در رشتهی جایگزین به گروههایی که با پرانتز در الگو مشخص شدهاند اشاره میکنند. <bdo><code>$1</code></bdo> توسط متنی که با اولین گروه تطبیق یافته جایگزین میشود، <bdo><code>$2</code></bdo> نیز با دومین گروه و الی آخر تا <bdo><code>$9</code></bdo>. تطبیق کلی را میتوان با <bdo><code>$&</code></bdo> مورد ارجاع قرار داد.</p>
<p>میتوان یک تابع را به جای رشته به عنوان آرگومان دوم متد <code>replace</code> ارسال کرد. برای هر جایگزینی، این تابع فراخوانی میشود درحالیکه دستهی تطبیق خورده (همچنین تطبیق کامل) به عنوان آرگومان به آن ارسال میشود و مقداری که برمیگرداند در رشتهی جدید قرار میگیرد.</p>
<p>به مثال کوچک زیر توجه نمایید:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_fwgl3+oeyX" href="#c_fwgl3+oeyX" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">s</span> <span class="cm-operator">=</span> <span class="cm-string">"the cia and fbi"</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">s</span>.<span class="cm-property">replace</span>(<span class="cm-string-2">/\b(fbi|cia)\b/g</span>,
<span class="cm-def">str</span> <span class="cm-operator">=></span> <span class="cm-variable-2">str</span>.<span class="cm-property">toUpperCase</span>()));
<span class="cm-comment">// → the CIA and FBI</span></pre>
<p>و مثالی جالبتر:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Zo/y2Vv93l" href="#c_Zo/y2Vv93l" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">stock</span> <span class="cm-operator">=</span> <span class="cm-string">"1 lemon, 2 cabbages, and 101 eggs"</span>;
<span class="cm-keyword">function</span> <span class="cm-def">minusOne</span>(<span class="cm-def">match</span>, <span class="cm-def">amount</span>, <span class="cm-def">unit</span>) {
<span class="cm-variable-2">amount</span> <span class="cm-operator">=</span> <span class="cm-variable">Number</span>(<span class="cm-variable-2">amount</span>) <span class="cm-operator">-</span> <span class="cm-number">1</span>;
<span class="cm-keyword">if</span> (<span class="cm-variable-2">amount</span> <span class="cm-operator">==</span> <span class="cm-number">1</span>) { <span class="cm-comment">// only one left, remove the 's'</span>
<span class="cm-variable-2">unit</span> <span class="cm-operator">=</span> <span class="cm-variable-2">unit</span>.<span class="cm-property">slice</span>(<span class="cm-number">0</span>, <span class="cm-variable-2">unit</span>.<span class="cm-property">length</span> <span class="cm-operator">-</span> <span class="cm-number">1</span>);
} <span class="cm-keyword">else</span> <span class="cm-keyword">if</span> (<span class="cm-variable-2">amount</span> <span class="cm-operator">==</span> <span class="cm-number">0</span>) {
<span class="cm-variable-2">amount</span> <span class="cm-operator">=</span> <span class="cm-string">"no"</span>;
}
<span class="cm-keyword">return</span> <span class="cm-variable-2">amount</span> <span class="cm-operator">+</span> <span class="cm-string">" "</span> <span class="cm-operator">+</span> <span class="cm-variable-2">unit</span>;
}
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">stock</span>.<span class="cm-property">replace</span>(<span class="cm-string-2">/(\d+) (\w+)/g</span>, <span class="cm-variable">minusOne</span>));
<span class="cm-comment">// → no lemon, 1 cabbage, and 100 eggs</span></pre>
<p>این مثال رشتهای را میگیرد، تمامی دفعات تکرار یک عدد که بعد از آن یک کاراکتر کلمه (منظور کاراکتری از جنس حرف و عدد است) آمده باشد را پیدا میکند و رشتهای برمی گرداند که در آن هر تطبیق پیدا شده یک واحد کاهش یافته است.</p>
<p>گروه <bdo><code>(\d+)</code></bdo> به عنوان آرگومان <code>amount</code> در تابع استفاده شده است، و گروه <bdo><code>(\w+)</code></bdo> به <code>unit</code> اختصاص یافته است. این تابع <code>amount</code> را به یک عدد تبدیل میکند – این عمل همیشه درست کار خواهد کرد چرا که توسط <bdo><code>\d+</code></bdo> تطبیق خورده است – و آن را در صورتی که فقط یک و صفر باقی مانده باشد، تغییراتی میدهد.</p>
<h2><a class="h_ident" id="h_BWZtpPcYWn" href="#h_BWZtpPcYWn" tabindex="-1" role="presentation"></a>عملگرهای حریصانه</h2>
<p>میتوان از متد <code>replace</code> برای نوشتن تابعی که همهی توضیحات را از یک قطعه کد جاوااسکریپت حذف کند استفاده نمود. اولین تلاش ما برای این کار به شکل زیر است:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_u0oKSJTOA2" href="#c_u0oKSJTOA2" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">stripComments</span>(<span class="cm-def">code</span>) {
<span class="cm-keyword">return</span> <span class="cm-variable-2">code</span>.<span class="cm-property">replace</span>(<span class="cm-string-2">/\/\/.*|\/\*[^]*\*\//g</span>, <span class="cm-string">""</span>);
}
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">stripComments</span>(<span class="cm-string">"1 + /* 2 */3"</span>));
<span class="cm-comment">// → 1 + 3</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">stripComments</span>(<span class="cm-string">"x = 10;// ten!"</span>));
<span class="cm-comment">// → x = 10;</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">stripComments</span>(<span class="cm-string">"1 /* a */+/* b */ 1"</span>));
<span class="cm-comment">// → 1 1</span></pre>
<p>قسمتی که قبل از عملگر یا (|) آمده است مطابق با دو کاراکتر <em>اسلشی</em> خواهد بود که میتواند بعد از آنها هر کاراکتری غیر از کاراکترهای خط جدید بیاید. بخشی که مربوط به توضیحات چندخطه میباشد کمی پیچیدهتر است. ما از <bdo><code>[^]</code></bdo> (به معنای هر کاراکتر که در یک مجموعهی تهی از کاراکترها جا نمیگیرد) به عنوان روشی برای تطبیق همهی کاراکترها استفاده کردهایم. نمیتوانیم فقط از یک نقطه (.) برای این منظور در اینجا استفاده کنیم چراکه بلاکهای کامنت را میتوان در چند خط نوشت و کاراکتر نقطه کاراکترهای خطوط جدید را تطبیق نمیدهد.</p>
<p>اما خروجی خط آخر به نظر میرسد که دارای اشتباه باشد. چرا؟</p>
<p>قسمت <bdo><code>[^]*</code></bdo> عبارت، همانطور که در قسمت عقبگرد توضیح دادم، در ابتدا تا آنجایی که میتواند تطبیق میدهد. اگر این کار منجر به این شود که بخش بعدی الگو شکست بخورد، تطبیقگر یک کاراکتر به عقب برگشته و از آن نقطه دوباره تلاش میکند. در مثال بالا، تطبیقگر ابتدا تلاش میکند تا کل رشتهی باقیمانده را تطبیق دهد سپس از آنجا به عقب برگردد. این موجب خواهد شد که یک نمونه از <bdo><code>*/</code></bdo> را بعد از اینکه چهار کاراکتر به عقب برمیگردد تطبیق دهد. این چیزی نیست که به دنبال آن بودیم – قصد ما این بود که یک توضیح را تطبیق دهیم، نه اینکه تا انتهای کدهای برنامه را برای پیدا کردن پایان آخرین بلاک توضیحات پیمایش کنیم.</p>
<p>به خاطر این عملکرد، به عملگرهای تکرار <bdo>(<code>+</code>, <code>*</code>, <code>?</code>, و <code>{}</code>)</bdo> عملگرهای <em>حریصانه</em> می گوییم، به این معنا که تا جایی که میتوانند تطبیق میدهند بعد به عقب برمیگردنند. اگر بعد از آن ها یک علامت سوال قرار دهید <bdo> (<code>+?</code>, <code>*?</code>, <code>??</code>, <code>{}?</code>)</bdo>، دیگر حریص نخواهند بود و با حداقل تطبیق شروع میکنند، زمانی به تطبیق بیشتر میپردازند که الگوی باقیمانده با تطبیقی کوچکتر مطابقت نداشته باشد.</p>
<p>و این دقیقا آن چیزی است که در این مورد آن را میخواهیم. با تطبیق کوچکترین بازههایی از کاراکترها به وسیلهی ستاره که مارا به یک <bdo><code>*/</code></bdo> برساند، ما فقط یک بلاک توضیحات را انتخاب کردیم و نه چیز بیشتری را.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_MCNF7GxfR1" href="#c_MCNF7GxfR1" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">stripComments</span>(<span class="cm-def">code</span>) {
<span class="cm-keyword">return</span> <span class="cm-variable-2">code</span>.<span class="cm-property">replace</span>(<span class="cm-string-2">/\/\/.*|\/\*[^]*?\*\//g</span>, <span class="cm-string">""</span>);
}
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">stripComments</span>(<span class="cm-string">"1 /* a */+/* b */ 1"</span>));
<span class="cm-comment">// → 1 + 1</span></pre>
<p>زمانی که یک عملگر غیرحریصانه کارکرد بهتری برای مسئله دارد، اگر بدون دلیل و آگاهی از یک عملگر حریصانه استفاده کنید، ممکن است با باگهای زیادی در برنامه روبرو شوید. هنگام استفاده از یک عملگر تکرار، بهتر است ابتدا به سراغ نسخهی غیر حریصانهی آن بروید.</p>
<h2><a class="h_ident" id="h_8lIV0MSfPq" href="#h_8lIV0MSfPq" tabindex="-1" role="presentation"></a>ساخت اشیاء RegExp به صورت پویا</h2>
<p>در بعضی مواقع، ممکن است هنگام کدنویسی، الگوی مورد نیاز جهت تطبیق مشخص نباشد. فرض کنید که میخواهید به دنبال نام کاربر در یک متن بگردید و آن را توسط یک جفت کاراکتر خط زیرین محصور کنید تا بتوان آن را شناسایی کرد. به دلیل اینکه فقط در هنگام اجرای برنامه نام مورد نظر مشخص میشود، نمیتوان از روش استفاده از اسلش بهره برد.</p>
<p>اما میتوانید یک رشته تولید کنید و از سازندهی <code>RegExp</code> روی آن استفاده کنید. به مثال توجه کنید:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_3yQimfD35d" href="#c_3yQimfD35d" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">name</span> <span class="cm-operator">=</span> <span class="cm-string">"harry"</span>;
<span class="cm-keyword">let</span> <span class="cm-def">text</span> <span class="cm-operator">=</span> <span class="cm-string">"Harry is a suspicious character."</span>;
<span class="cm-keyword">let</span> <span class="cm-def">regexp</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">RegExp</span>(<span class="cm-string">"\\b("</span> <span class="cm-operator">+</span> <span class="cm-variable">name</span> <span class="cm-operator">+</span> <span class="cm-string">")\\b"</span>, <span class="cm-string">"gi"</span>);
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">text</span>.<span class="cm-property">replace</span>(<span class="cm-variable">regexp</span>, <span class="cm-string">"_$1_"</span>));
<span class="cm-comment">// → _Harry_ is a suspicious character.</span></pre>
<p>هنگام نوشتن نشانگرهای (مرز) <bdo><code>\b</code></bdo> ، باید از دو بکاسلش استفاده کنیم به این علت که آنها را در یک رشتهی نرمال مینویسیم نه یک عبارت باقاعده که توسط اسلش محصور شده است. آرگومان دوم سازندهی <code>RegExp</code> مربوط به گزینههای مربوط به عبارت باقاعده است – در این مثال، <code>"gi"</code> برای مشخص کردن سراسری بودن و غیرحساس بودن به حروف بزرگ و کوچک است.</p>
<p>اما چه میشود اگر نام کاربر مورد نظر <bdo><code>"dea+hl[]rd"</code></bdo> باشد که متعلق یک نوجوان خورهی کامپیوتر است؟ این نام باعث میشود که یک عبارت باقاعدهی بیمعنا تولید شود که منجر به تطبیق نام کاربر نمیشود.</p>
<p>راه حل این مشکل، اضافه کردن بکاسلش قبل از هر کاراکتری که معنای خاصی دارد است.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_VVThPW6YGV" href="#c_VVThPW6YGV" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">name</span> <span class="cm-operator">=</span> <span class="cm-string">"dea+hl[]rd"</span>;
<span class="cm-keyword">let</span> <span class="cm-def">text</span> <span class="cm-operator">=</span> <span class="cm-string">"This dea+hl[]rd guy is super annoying."</span>;
<span class="cm-keyword">let</span> <span class="cm-def">escaped</span> <span class="cm-operator">=</span> <span class="cm-variable">name</span>.<span class="cm-property">replace</span>(<span class="cm-string-2">/[\\[.+*?(){|^$]/g</span>, <span class="cm-string">"\\$&"</span>);
<span class="cm-keyword">let</span> <span class="cm-def">regexp</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">RegExp</span>(<span class="cm-string">"\\b"</span> <span class="cm-operator">+</span> <span class="cm-variable">escaped</span> <span class="cm-operator">+</span> <span class="cm-string">"\\b"</span>, <span class="cm-string">"gi"</span>);
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">text</span>.<span class="cm-property">replace</span>(<span class="cm-variable">regexp</span>, <span class="cm-string">"_$&_"</span>));
<span class="cm-comment">// → This _dea+hl[]rd_ guy is super annoying.</span></pre>
<h2><a class="h_ident" id="h_6SlL1IdtMU" href="#h_6SlL1IdtMU" tabindex="-1" role="presentation"></a>متد search</h2>
<p><a class="p_ident" id="p_NWoZK3kTsE" href="#p_NWoZK3kTsE" tabindex="-1" role="presentation"></a>متد <code>indexOf</code> که روی رشتهها کار میکرد را نمیتوان با یک عبارت باقاعده فراخواند. اما متد دیگری به نام <code>search</code> وجود دارد که یک عبارت باقاعده را دریافت میکند. درست مانند <code>indexOf</code>، این متد نیز اولین خانهی خروجی را به عبارتی که پیدا شد اختصاص میدهد و یا در صورت پیدا نکردن نتیجه، <bdo>-1</bdo> را برمیگرداند.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_diUfxE6ifs" href="#c_diUfxE6ifs" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">" word"</span>.<span class="cm-property">search</span>(<span class="cm-string-2">/\S/</span>));
<span class="cm-comment">// → 2</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">" "</span>.<span class="cm-property">search</span>(<span class="cm-string-2">/\S/</span>));
<span class="cm-comment">// → -1</span></pre>
<p>متاسفانه، راهی برای مشخص کردن نقطهی شروع برای تطبیق وجود ندارد (شبیه کاری که میتوانیم با آرگومان دوم <code>indexOf</code> انجام دهیم) که در صورت وجود کاربرد داشت.</p>
<h2><a class="h_ident" id="h_EzWBx9Vn7i" href="#h_EzWBx9Vn7i" tabindex="-1" role="presentation"></a>خاصیت lastIndex</h2>
<p>متد <code>exec</code> نیز راهی مناسب برای شروع جستجو از یک موقعیت داده شده در یک رشته را پشتیبانی نمیکند. اما یک راه غیر سرراست برای این کار وجود دارد.</p>
<p>اشیائی که از نوع عبارت باقاعده هستند دارای یک سری خاصیت میباشند. یکی از این خاصیتها <code>source</code> است، که رشتهای که عبارت از آن تولید شده است را نگهداری میکند. یک خاصیت دیگر، <code>lastIndex</code> است که در شرایط محدودی کنترل میکند که تطبیق بعدی از کجا شروع خواهد شد.</p>
<p>آن شرایط این است که عبارت باقاعده باید گزینههای سراسری (<code>g</code>) یا چسبنده (<code>y</code>) را فعال داشته باشد و تطبیق باید با متد <code>exec</code> صورت پذیرد. باز هم یک راه حل کمتر گیجکننده میتوانست این باشد که اجازه داده شود که یک آرگومان اضافی برای این کار به متد <code>exec</code> فرستاده میشود، اما گیج کنندگی، یکی از ویژگیهای اساسی رابط عبارات باقاعده در جاوااسکریپت است.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_nXsHtqIJdF" href="#c_nXsHtqIJdF" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">pattern</span> <span class="cm-operator">=</span> <span class="cm-string-2">/y/g</span>;
<span class="cm-variable">pattern</span>.<span class="cm-property">lastIndex</span> <span class="cm-operator">=</span> <span class="cm-number">3</span>;
<span class="cm-keyword">let</span> <span class="cm-def">match</span> <span class="cm-operator">=</span> <span class="cm-variable">pattern</span>.<span class="cm-property">exec</span>(<span class="cm-string">"xyzzy"</span>);
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">match</span>.<span class="cm-property">index</span>);
<span class="cm-comment">// → 4</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">pattern</span>.<span class="cm-property">lastIndex</span>);
<span class="cm-comment">// → 5</span></pre>
<p>اگر تطبیق با موفقیت انجام شد، فراخوانی <code>exec</code> به طور خودکار خاصیت <code>lastIndex</code> را به روزرسانی کرده تا به نقطهی بعد از تطبیق اشاره کند. اگر تطبیقی پیدا نشود، <code>lastIndex</code> مقدار صفر را خواهد گرفت، که مقداری است که شیء در هنگام ایجاد یک عبارات باقاعده جدید نگهداری میکند.</p>
<p>تفاوت بین گزینهی سراسری و چسبنده این است که در حالت فعال بودن گزینهی چسبنده، زمانی تطبیق موفق خواهد بود که مستقیما از نقطهی <code>lastIndex</code> شروع شود درحالیکه در حالت سراسری، جستجو رو به جلو انجام خواهد شد تا به موقعیتی برسد که یک تطبیق بتواند شروع شود.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_98GwGRIMj8" href="#c_98GwGRIMj8" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">global</span> <span class="cm-operator">=</span> <span class="cm-string-2">/abc/g</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">global</span>.<span class="cm-property">exec</span>(<span class="cm-string">"xyz abc"</span>));
<span class="cm-comment">// → ["abc"]</span>
<span class="cm-keyword">let</span> <span class="cm-def">sticky</span> <span class="cm-operator">=</span> <span class="cm-string-2">/abc/y</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">sticky</span>.<span class="cm-property">exec</span>(<span class="cm-string">"xyz abc"</span>));
<span class="cm-comment">// → null</span></pre>
<p>اگر از یک عبارت باقاعدهی مشترک برای چندین فراخوانی <code>exec</code> استفاده کنیم این بهروزرسانیهای خودکار خاصیت <code>lastIndex</code> میتواند مشکلساز باشد. عبارت باقاعدهی شما ممکن است تصادفا از اندیسی شروع شود که از فراخوانی قبلی به جا مانده باشد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_wrx2wO0P8M" href="#c_wrx2wO0P8M" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">digit</span> <span class="cm-operator">=</span> <span class="cm-string-2">/\d/g</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">digit</span>.<span class="cm-property">exec</span>(<span class="cm-string">"here it is: 1"</span>));
<span class="cm-comment">// → ["1"]</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">digit</span>.<span class="cm-property">exec</span>(<span class="cm-string">"and now: 1"</span>));
<span class="cm-comment">// → null</span></pre>
<p>یک اثر جالب توجه دیگر در صورت استفاده از گزینهی سراسری این است که باعث میشود کارکرد متد <code>match</code> روی رشتهها، متفاوت باشد. زمانی که این متد با عبارتی سراسری فراخوانی شود، به جای اینکه آرایهای شبیه چیزی که از <code>exec</code> برگردانده میشد تولید کند، متد <code>match</code> تمامی تطبیقهای الگوی درون رشته را پیدا میکند و آرایهای حاوی تمام رشتههای تطبیق خورده تولید میکند.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_weT/d5+8vE" href="#c_weT/d5+8vE" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Banana"</span>.<span class="cm-property">match</span>(<span class="cm-string-2">/an/g</span>));
<span class="cm-comment">// → ["an", "an"]</span></pre>
<p>بنابراین با احتیاط سراغ عبارات باقاعدهی سراسری بروید. معمولا تنها مواردی که لازم است به سراغ آنها بروید هنگامی است که به فراخوانی متد <code>replace</code> نیاز دارید و همچنین مواقعی که لازم است تا صراحتا از <code>lastIndex</code> استفاده کنید.</p>
<h3><a class="i_ident" id="i_pR8PYScPxZ" href="#i_pR8PYScPxZ" tabindex="-1" role="presentation"></a>پیمایش تطبیقها</h3>
<p>یکی از کارهای رایج این است که تمامی موارد رخداد یک الگو در رشته را در بدنهی حلقه پیمایش کنیم به شکلی که شیء تطبیق داده شده در دسترس ما باشد. برای اینکار میتوانیم از متدهای <code>lastIndex</code> و <code>exec</code> استفاده کنیم.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_rSzEnbVHja" href="#c_rSzEnbVHja" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">input</span> <span class="cm-operator">=</span> <span class="cm-string">"A string with 3 numbers in it... 42 and 88."</span>;
<span class="cm-keyword">let</span> <span class="cm-def">number</span> <span class="cm-operator">=</span> <span class="cm-string-2">/\b\d+\b/g</span>;
<span class="cm-keyword">let</span> <span class="cm-def">match</span>;
<span class="cm-keyword">while</span> (<span class="cm-variable">match</span> <span class="cm-operator">=</span> <span class="cm-variable">number</span>.<span class="cm-property">exec</span>(<span class="cm-variable">input</span>)) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Found"</span>, <span class="cm-variable">match</span>[<span class="cm-number">0</span>], <span class="cm-string">"at"</span>, <span class="cm-variable">match</span>.<span class="cm-property">index</span>);
}
<span class="cm-comment">// → Found 3 at 14</span>
<span class="cm-comment">// Found 42 at 33</span>
<span class="cm-comment">// Found 88 at 40</span></pre>
<p>این مثال از این واقعیت استفاده میکند که مقدار یک عبارت تخصیص (<code>=</code>)، همان مقدار انتساب داده شده است. بنابراین با استفاده از <bdo><code>match = number.<wbr>exec(input)</code></bdo> به عنوان قسمت شرط دستور <code>while</code>، تطبیق را در شروع هر تکرار حلقه اجرا میکنیم و نتیجهی آن را در یک متغیر ذخیره میکنیم، و هنگامی پیمایش حلقه را متوقف میکنیم که تطبیقی پیدا نشود.</p>
<h2 id="ini"><a class="h_ident" id="h_KEUpkgIz70" href="#h_KEUpkgIz70" tabindex="-1" role="presentation"></a>تجزیهی یک فایل ini</h2>
<p>برای به پایان رساندن این فصل، به سراغ مسئلهای میرویم که به دست عبارات باقاعده حل میشود. فرض کنید که در حال نوشتن برنامهای هستیم که به طور خودکار اطلاعاتی دربارهی دشمنانمان از سطح اینترنت جمع آوری میکند. (واقعا قرار نیست این برنامه را در اینجا بنویسیم، فقط بخشی را مینویسیم که فایل حاوی تنظیمات را میخواند. از این بابت متاسفم.) فایل تنظیمات به این شکل است:</p>
<pre class="snippet cm-s-default" data-language="text/plain" ><a class="c_ident" id="c_RV3f5fiptq" href="#c_RV3f5fiptq" tabindex="-1" role="presentation"></a>searchengine=https://duckduckgo.com/?q=$1
spitefulness=9.7
; comments are preceded by a semicolon...
; each section concerns an individual enemy
[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_5+sfP6l0gN" href="#p_5+sfP6l0gN" tabindex="-1" role="presentation"></a>قوانین حاکم بر این فایل (که فرمتی بسیار رایج است و معمولا یک فایل INI نامیده میشود) به صورت زیر است:</p>
<ul>
<li>
<p>خطوط خالی و خطهایی که با نقطهویرگول شروع میشوند صرف نظر میشوند.</p></li>
<li>
<p>خطوطی که بین <bdo><code>[</code></bdo> و <bdo><code>]</code></bdo> محصور هستند یک بخش جدید را شروع میکنند.</p></li>
<li>
<p>خطوطی که حاوی یک شناسهی عددی-حرفی هستند که بعد از آن کاراکتر <code>=</code> میآید، یک گزینه به تنظیمات بخش فعلی اضافه میکنند.</p></li>
<li>
<p>هر چیز دیگری غیر از موارد بالا نامعتبر شناخته میشود.</p></li></ul>
<p>وظیفهی ما این است که رشتهای شبیه این را به یک شیء تبدیل کنیم که خاصیتهایش رشتههای تنظیمات نوشته شده قبل از اولین بخش را نگهداری میکنند و زیرشیءهایش به بخشهایی تعلق دارند که هر زیرشیء تنظیمات یک بخش را در خود دارد.</p>
<p>به دلیل اینکه این فرمت باید خط به خط پردازش شود، تقسیم فایل به خطوط مجزا شروع خوبی به نظر میرسد. ما متد <code>split</code> را در <a href="04_data.html#split">فصل 4</a> دیدیم. بعضی سیستم عاملها، به هر دلیلی، فقط از کاراکتر خط جدید برای جداسازی خطوط استفاده نمیکنند بلکه از یک کاراکتر بازگشت به ابتدای خط و بعد از آن کاراکتر خط جدید برای این کار استفاده میکنند <bdo>(<code>"\r\n"</code>)</bdo>. با درنظر گرفتن اینکه میدانیم میتوان به متد <code>split</code>، یک عبارات باقاعده ارسال کرد میتوانیم جداسازی خطوط را با عبارت باقاعده ای شبیه <bdo><code>/\r?\n/</code></bdo> انجام دهیم که باعث میشود هم <bdo><code>"\n"</code></bdo> و هم <bdo><code>"\r\n"</code></bdo> در نظر گرفته شود.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_neI86/XXg2" href="#c_neI86/XXg2" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">parseINI</span>(<span class="cm-def">string</span>) {
<span class="cm-comment">// Start with an object to hold the top-level fields</span>
<span class="cm-keyword">let</span> <span class="cm-def">result</span> <span class="cm-operator">=</span> {};
<span class="cm-keyword">let</span> <span class="cm-def">section</span> <span class="cm-operator">=</span> <span class="cm-variable-2">result</span>;
<span class="cm-variable-2">string</span>.<span class="cm-property">split</span>(<span class="cm-string-2">/\r?\n/</span>).<span class="cm-property">forEach</span>(<span class="cm-def">line</span> <span class="cm-operator">=></span> {
<span class="cm-keyword">let</span> <span class="cm-def">match</span>;
<span class="cm-keyword">if</span> (<span class="cm-variable-2">match</span> <span class="cm-operator">=</span> <span class="cm-variable-2">line</span>.<span class="cm-property">match</span>(<span class="cm-string-2">/^(\w+)=(.*)$/</span>)) {
<span class="cm-variable-2">section</span>[<span class="cm-variable-2">match</span>[<span class="cm-number">1</span>]] <span class="cm-operator">=</span> <span class="cm-variable-2">match</span>[<span class="cm-number">2</span>];
} <span class="cm-keyword">else</span> <span class="cm-keyword">if</span> (<span class="cm-variable-2">match</span> <span class="cm-operator">=</span> <span class="cm-variable-2">line</span>.<span class="cm-property">match</span>(<span class="cm-string-2">/^\[(.*)\]$/</span>)) {
<span class="cm-variable-2">section</span> <span class="cm-operator">=</span> <span class="cm-variable-2">result</span>[<span class="cm-variable-2">match</span>[<span class="cm-number">1</span>]] <span class="cm-operator">=</span> {};
} <span class="cm-keyword">else</span> <span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-string-2">/^\s*(;.*)?$/</span>.<span class="cm-property">test</span>(<span class="cm-variable-2">line</span>)) {
<span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">Error</span>(<span class="cm-string">"Line '"</span> <span class="cm-operator">+</span> <span class="cm-variable-2">line</span> <span class="cm-operator">+</span> <span class="cm-string">"' is not valid."</span>);
}
});
<span class="cm-keyword">return</span> <span class="cm-variable-2">result</span>;
}
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">parseINI</span>(<span class="cm-string-2">`</span>
<span class="cm-string-2">name=Vasilis</span>
<span class="cm-string-2">[address]</span>
<span class="cm-string-2">city=Tessaloniki`</span>));
<span class="cm-comment">// → {name: "Vasilis", address: {city: "Tessaloniki"}}</span></pre>
<p>کد بالا به این صورت عمل میکند که خط به خط فایل را پردازش کرده و یک شیء میسازد. خاصیتهای قسمت بالایی مستقیما درون شیء ذخیره میشوند، درحالیکه خاصیتهایی که در بخشها قرار دارند به صورت جداگانه در شیئی مختص هر بخش قرار میگیرند. متغیر <code>section</code> به شیء بخش کنونی اشاره میکند.</p>
<p>دو نوع قابل توجه خط وجود دارد – سرتیترهای بخش یا خطوط خاصیتها. زمانی که یک خط معرف یک خاصیت معمولی است، در بخش فعلی ذخیره میشود. زمانی که معرف یک سرتیتر بخش است، یک شیء جدید برای بخش مورد نظر ایجاد میشود و <code>section</code> به آن تخصیص مییابد.</p>
<p>توجه داشته باشید که استفادهی مکرر از <code>^</code> و <code>$</code> برای این است که مطمئن شویم عبارت تمام خط را تطبیق میدهد نه فقط بخشی از آن را. اگر از آنها استفاده نشود، کد در اکثر مواقع کار میکند اما برای بعضی ورودیها رفتار عجیبی از خود نشان دهد که ممکن است اشکال زدایی آن سخت باشد.</p>
<p><a class="p_ident" id="p_lY9X9X1yow" href="#p_lY9X9X1yow" tabindex="-1" role="presentation"></a>الگوی <bdo><code>if (match = string.<wbr>match(.<wbr>.<wbr>.<wbr>))</code></bdo> شبیه به ترفندی است که از عبارت تخصیص به عنوان شرط <code>while</code> استفاده کردیم. اغلب اطمینان ندارید که فراخوانی <code>match</code> موفق خواهد شد، بنابراین میتوانید فقط درون یک دستور if که آن را آزمایش میکند به نتیجهی آن دسترسی داشته باشید. برای جلوگیری از شکستن زنجیرهی <bdo><code>else if</code></bdo>، نتیجهی تطبیق را به متغیری اختصاص دادیم و بلافاصله آن تخصیص را به عنوان شرط دستور <code>if</code> استفاده کردهایم.</p>
<p>اگر یک خط، سرتیتر بخش یا یک خاصیت نباشد، تابع با استفاده از عبارت <bdo><code>/^\s*(;.*)?$/</code></bdo> بررسی میکند که آیا این خط توضیح است یا خطی خالی. متوجه نحوهی کارکرد آن شدید؟ قسمتی که داخل پرانتز است توضیحات را تطبیق میدهد و علامت سوال <code>?</code> اطمینان حاصل میکند که خطوطی که فقط فضای خالی هستند شناسایی شوند. اگر خطی با هیچکدام از اشکال قابل انتظار تطبیق نخورد، تابع یک استثنا تولید میکند.</p>
<h2><a class="h_ident" id="h_TW2GNFCWIU" href="#h_TW2GNFCWIU" tabindex="-1" role="presentation"></a>کاراکترهای بینالمللی</h2>
<p><a class="p_ident" id="p_iHMJ0Ei+74" href="#p_iHMJ0Ei+74" tabindex="-1" role="presentation"></a>به دلیل اینکه پیادهسازی اولیه جاوااسکریپت بسیار ساده بوده است و این واقعیت که این شیوهی ساده محور بعدها به عنوان یک استاندارد رفتاری در نظر گرفته شد، عبارات باقاعده در جاوااسکریپت نسبتا برای کاراکترهای غیر انگلیسی، حرفی برای گفتن ندارند. به عنوان مثال، در عبارات باقاعده جاوااسکریپت، یک "کاراکتر کلمه" فقط شامل 26 حرف لاتین (حروف بزرگ و کوچک)، اعداد دهدهی، و به دلایلی کاراکتر خط زیرین میشود. چیزهایی مثل <em>é</em> یا <em>β</em> که قطعا کاراکتر کلمه محسوب میشوند توسط <bdo><code>\w</code></bdo> تطبیق نمیخورند (و با <bdo><code>\W</code></bdo> تطبیق میخورند، دستهی کاراکترهای غیر کلمه).</p>
<p>به خاطر یک اتفاق نامعلوم در گذشته، <bdo><code>\s</code></bdo> (فضای خالی) این مشکل را ندارد و همهی کاراکترهایی که استاندارد یونیکد به عنوان فضای خالی درنظر میگیرد را شامل میشود، مثل کاراکترهایی از قبیل نیمفاصله و جداکننده حروف صدادار در زبان مغولی.</p>
<p>مشکل دیگر این است که به طور پیش فرض عبارات باقاعده روی واحدهای کد عمل میکنند؛ نه روی کاراکترهای واقعی؛ همانطور که در <a href="05_higher_order.html#code_units">فصل 5</a> بحث شد. معنای آن این است که با کاراکترهایی که از دو واحد کد تشکیل شدهاند به شکل نامشخصی رفتار میشود.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_CfMTYxun8D" href="#c_CfMTYxun8D" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/🍎{3}/</span>.<span class="cm-property">test</span>(<span class="cm-string">"🍎🍎🍎"</span>));
<span class="cm-comment">// → false</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/<.>/</span>.<span class="cm-property">test</span>(<span class="cm-string">"<🌹>"</span>));
<span class="cm-comment">// → false</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/<.>/u</span>.<span class="cm-property">test</span>(<span class="cm-string">"<🌹>"</span>));
<span class="cm-comment">// → true</span></pre>
<p>مشکل اینجاست که 🍎 در خط اول به عنوان دو واحد کد شناخته میشود، و <bdo><code>{3}</code></bdo> فقط به واحد دوم اعمال میشود. به طور مشابه، عملگر نقطه فقط یک واحد کد را میشناسد نه دو واحدی که ایموجی گل رز را میسازند.</p>
<p>برای اینکه عبارت باقاعده این گونه کاراکترها را در نظر بگیرد باید گزینهی <code>u</code> (یونیکد) را استفاده کنید. متاسفانه به صورت پیشفرض این اشکال وجود خواهد داشت چون تغییر آن ممکن است مشکلاتی را برای کدهای نوشته شده از قبل که به این رفتار وابستگی دارند به وجود بیاورد.</p>
<p>اگرچه این قضیه به تازگی استاندارد شده است، و در هنگام نوشتن این کتاب، هنوز به طور گسترده از آن پشتیبانی نمیشود، میتوان از <bdo><code>\p</code></bdo> در یک عبارت باقاعده (عبارتی که باید گزینهی یونیکد را فعال داشته باشد) برای تطبیق همهی کاراکترهایی که استاندارد یونیکد برای آنها خاصیتی در نظر گرفته است، استفاده کرد.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_+jV1oln0sr" href="#c_+jV1oln0sr" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/\p{Script=Greek}/u</span>.<span class="cm-property">test</span>(<span class="cm-string">"α"</span>));
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/\p{Script=Arabic}/u</span>.<span class="cm-property">test</span>(<span class="cm-string">"α"</span>));
<span class="cm-comment">// → false</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/\p{Alphabetic}/u</span>.<span class="cm-property">test</span>(<span class="cm-string">"α"</span>));
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">/\p{Alphabetic}/u</span>.<span class="cm-property">test</span>(<span class="cm-string">"!"</span>));
<span class="cm-comment">// → false</span></pre>
<p>یونیکد تعدادی خاصیت مفید تعریف میکند، اگرچه پیدا کردن خاصیتی که نیاز شما باشد ممکن است که همیشه ساده نباشد. میتوانید از دستور <bdo><code>\p{Property=Value}</code></bdo> برای تطبیق هر کاراکتری که مقدار داده شده را برای آن خاصیت داشته باشد استفاده کنید. اگر نام خاصیت را همانطور که در <bdo><code>\p{Name}</code></bdo> میبینید حذف کنیم، نام آن یا به عنوان یک خاصیت دودویی مثل <code>Alphabetic</code> در نظر گرفته میشود یا یک دسته مثل <code>Number</code>.</p>
<h2 id="summary_regexp"><a class="h_ident" id="h_EzvDUHyjs2" href="#h_EzvDUHyjs2" tabindex="-1" role="presentation"></a>خلاصه</h2>
<p>عبارات باقاعده اشیائی هستند که الگوها را در رشتهها نشان میدهند. این عبارات از زبانی مخصوص به خود برای بیان این الگوها استفاده میکنند.</p>
<table>
<tr><td><bdo><code>/abc/</code></bdo></td><td>یک دنباله از کاراکترها</td>
</tr>
<tr><td><bdo><code>/[abc]/</code></bdo></td><td>یک کاراکتر از یک مجموعه کاراکتر</td>
</tr>
<tr><td><bdo><code>/[^abc]/</code></bdo></td><td>یک کاراکتر که در مجموعهی مشخص شده نباشد</td>
</tr>
<tr><td><bdo><code>/[0-9]/</code></bdo></td><td>یک کاراکتر که در یک بازه از کاراکترها قرار دارد</td>
</tr>
<tr><td><bdo><code>/x+/</code></bdo></td><td>یک یا بیش از یک بار وقوع الگوی <code>x</code></td>
</tr>
<tr><td><bdo><code>/x+?/</code></bdo></td><td>یک یا بیش از یک بار وقوع به صورت غیر حریصانه</td>
</tr>
<tr><td><bdo><code>/x*/</code></bdo></td><td>صفر یا بیش از صفر بار وقوع الگوی <code>x</code></td>
</tr>
<tr><td><bdo><code>/x?/</code></bdo></td><td>صفر یا یک بار وقوع</td>
</tr>
<tr><td><bdo><code>/x{2,4}/</code></bdo></td><td>دو تا چهار بار وقوع</td>
</tr>
<tr><td><bdo><code>/(abc)/</code></bdo></td><td>یک دسته یا گروه</td>
</tr>
<tr><td><bdo><code>/a|b|c/</code></bdo></td><td>یکی از الگوهای متعدد</td>
</tr>
<tr><td><bdo><code>/\d/</code></bdo></td><td>یک کاراکتر رقمی (عدد)</td>
</tr>
<tr><td><bdo><code>/\w/</code></bdo></td><td>یک کاراکتر حرف-عددی (یک کاراکتر کلمه)</td>
</tr>
<tr><td><bdo><code>/\s/</code></bdo></td><td>یک کاراکتر فضای خالی (هر نوعی)</td>
</tr>
<tr><td><bdo><code>/./</code></bdo></td><td>هر کاراکتری به جز کاراکتر خط جدید</td>
</tr>
<tr><td><bdo><code>/\b/</code></bdo></td><td>یک مرز کلمه</td>
</tr>
<tr><td><bdo><code>/^/</code></bdo></td><td>شروع ورودی</td>
</tr>
<tr><td><bdo><code>/$/</code></bdo></td><td>پایان ورودی</td>
</tr>
</table>
<p>یک عبارت باقاعده دارای متدی به نام <code>test</code> است که رشتهی داده شده را جهت تطبیق با عبارت بررسی میکند. همچنین متدی به نام <code>exec</code> دارد که در صورت پیدا کردن تطبیق، آرایهای تولید میکند که همهی گروههای تطبیق خورده را در بر دارد. این آرایه دارای خاصیتی به نام <code>index</code> است که نقطهی شروع تطبیق را مشخص میکند.</p>
<p>رشتهها دارای متدی به نام <code>match</code> میباشند که برای تطبیق آنها با یک عبارات باقاعده استفاده میشود. متدی به نام <code>search</code> دارند که برای جستجوی یک عبارت استفاده میشود که تنها موقعیت شروع تطبیق یافته شده را برمیگرداند. متد <code>replace</code> متعلق به رشتهها میتواند تطبیقهای پیدا شده برای یک الگو را با یک رشته یا تابع جایگزین کند.</p>
<p>عبارات باقاعده میتوانند گزینههایی هم داشته باشند که بعد از اسلش پایانی نوشته میشوند. گزینهی <code>i</code> باعث میشود که تطبیق به بزرگی و کوچکی حروف حساس نباشد. گزینهی <code>g</code> عبارت را <em>سراسری</em> میکند که علاوه بر نتایج دیگر، در متد <code>replace</code> باعث میشود که همهی نمونهها جایگزین شوند نه فقط اولین مورد. گزینهی <code>y</code> باعث میشود که عبارت چسبنده شود، که معنای آن این است که به سمت جلو جستجو نخواهد کرد و بخشی از رشته را در هنگام جستجو برای تطبیق در نظر نمیگیرد. گزینهی <code>u</code> حالت یونیکد را فعال میکند که مشکلات مربوط به کاراکترهایی که دو واحد کد اشغال میکنند را برطرف میکند.</p>
<p>عبارتهای باقاعده مانند چاقوی تیزی هستند که دستهی نامناسبی دارند. بعضی از کارها را به شدت ساده میکنند اما زمانی که به مسائل پیچیده اعمال میشوند میتوانند به سرعت غیر قابل کنترل شوند. بخشی از فرهنگ صحیح استفاده از عبارات باقاعده این است که برای چیزهایی که به روشنی به وسیلهی آنها قابل بیان نیستند به سراغشان نرویم.</p>
<h2><a class="h_ident" id="h_ggOFdVwDCk" href="#h_ggOFdVwDCk" tabindex="-1" role="presentation"></a>تمرینها</h2>
<p><a class="p_ident" id="p_ZLZe6zjlTr" href="#p_ZLZe6zjlTr" tabindex="-1" role="presentation"></a>تقریبا غیر قابل اجتناب است که در حین انجام تمرینهای این فصل، با دیدن بعضی از رفتارهای پیچیدهی عبارات باقاعده، دچار سردرگمی و ناامیدی نشوید. گاهی اوقات بهتر است که عبارتتان را در ابزارهای آنلاینی مثل <a href="https://www.debuggex.com/"><em>https://debuggex.com</em></a> وارد کنید تا ببینید تجسم عبارتتان با آنچه در نظر داشتهاید ارتباط دارد یا خیر و با توجه به واکنش آن رشتههای ورودی متفاوتی را آزمایش کنید.</p>
<h3><a class="i_ident" id="i_mjMEG+9L3E" href="#i_mjMEG+9L3E" tabindex="-1" role="presentation"></a>گلف Regexp</h3>
<p><a class="p_ident" id="p_modNywux3k" href="#p_modNywux3k" tabindex="-1" role="presentation"></a><em>گلف کد</em> اصطلاحی است که برای تلاش نوشتن برنامهای با حداقل کاراکتر استفاده میشود. به طور مشابه <em>regexp golf</em>، تمرین نوشتن کوتاهترین عبارت باقاعدهای است که برای تطبیق یک الگوی داده شده میتوان نوشت و <em>فقط</em> همان الگو باید تطبیق بخورد.</p>
<p>برای هر یک از آیتمهای زیر، یک عبارت باقاعده بنویسید و آزمایش کنید هر کدام از زیررشتههای داده شده در آن وقوع دارد یا خیر. عبارت باقاعدهای که مینویسید باید فقط رشتههایی را تطبیق دهد که یکی از زیر رشتههای داده شده را داشته باشند. نیازی نیست نگران مرزهای کلمات باشید مگر اینکه به طور صریح ذکر شده باشد. وقتی عبارت باقاعدهی شما به طور صحیح کار کرد، ببینید میتوانید آن را کوتاهتر بنویسید؟</p>
<ol>
<li>
<p><a class="p_ident" id="p_xvWnbeToqT" href="#p_xvWnbeToqT" tabindex="-1" role="presentation"></a><em>car</em> و <em>cat</em></p></li>
<li>
<p><a class="p_ident" id="p_GyRYO2ZOIs" href="#p_GyRYO2ZOIs" tabindex="-1" role="presentation"></a><em>pop</em> و <em>prop</em></p></li>
<li>
<p><a class="p_ident" id="p_NCSfDeF7SR" href="#p_NCSfDeF7SR" tabindex="-1" role="presentation"></a><em>ferret</em>, <em>ferry</em>, و <em>ferrari</em></p></li>
<li>
<p><a class="p_ident" id="p_mTg3ONUIhL" href="#p_mTg3ONUIhL" tabindex="-1" role="presentation"></a>هر کلمهای که با <em>ious</em> پایان پذیرد</p></li>
<li>
<p>یک کاراکتر فضای خالی که بعد از نقطه، ویرگول، دونقطه، یا نقطهویرگول بیاید</p></li>
<li>
<p>کلمهای که از شش حرف بیشتر باشد</p></li>
<li>
<p><a class="p_ident" id="p_LnjCe/FXPh" href="#p_LnjCe/FXPh" tabindex="-1" role="presentation"></a>یک کلمه بدون داشتن حرف <em>e</em> (یا <em>E</em>)</p></li>
</ol>
<p>به جدولی که در <a href="09_regexp.html#summary_regexp">خلاصه فصل</a> آمده است برای کمک گرفتن رجوع کنید. هر راه حل را با چندین رشتهی آزمایشی بررسی کنید.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Ilu9sT4yKB" href="#c_Ilu9sT4yKB" tabindex="-1" role="presentation"></a><span class="cm-comment">// Fill in the regular expressions</span>
<span class="cm-variable">verify</span>(<span class="cm-string-2">/.../</span>,
[<span class="cm-string">"my car"</span>, <span class="cm-string">"bad cats"</span>],
[<span class="cm-string">"camper"</span>, <span class="cm-string">"high art"</span>]);
<span class="cm-variable">verify</span>(<span class="cm-string-2">/.../</span>,
[<span class="cm-string">"pop culture"</span>, <span class="cm-string">"mad props"</span>],
[<span class="cm-string">"plop"</span>, <span class="cm-string">"prrrop"</span>]);
<span class="cm-variable">verify</span>(<span class="cm-string-2">/.../</span>,
[<span class="cm-string">"ferret"</span>, <span class="cm-string">"ferry"</span>, <span class="cm-string">"ferrari"</span>],
[<span class="cm-string">"ferrum"</span>, <span class="cm-string">"transfer A"</span>]);
<span class="cm-variable">verify</span>(<span class="cm-string-2">/.../</span>,
[<span class="cm-string">"how delicious"</span>, <span class="cm-string">"spacious room"</span>],
[<span class="cm-string">"ruinous"</span>, <span class="cm-string">"consciousness"</span>]);
<span class="cm-variable">verify</span>(<span class="cm-string-2">/.../</span>,
[<span class="cm-string">"bad punctuation ."</span>],
[<span class="cm-string">"escape the period"</span>]);
<span class="cm-variable">verify</span>(<span class="cm-string-2">/.../</span>,
[<span class="cm-string">"hottentottententen"</span>],
[<span class="cm-string">"no"</span>, <span class="cm-string">"hotten totten tenten"</span>]);
<span class="cm-variable">verify</span>(<span class="cm-string-2">/.../</span>,
[<span class="cm-string">"red platypus"</span>, <span class="cm-string">"wobbling nest"</span>],
[<span class="cm-string">"earth bed"</span>, <span class="cm-string">"learning ape"</span>, <span class="cm-string">"BEET"</span>]);
<span class="cm-keyword">function</span> <span class="cm-def">verify</span>(<span class="cm-def">regexp</span>, <span class="cm-def">yes</span>, <span class="cm-def">no</span>) {
<span class="cm-comment">// Ignore unfinished exercises</span>
<span class="cm-keyword">if</span> (<span class="cm-variable-2">regexp</span>.<span class="cm-property">source</span> <span class="cm-operator">==</span> <span class="cm-string">"..."</span>) <span class="cm-keyword">return</span>;
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">str</span> <span class="cm-keyword">of</span> <span class="cm-variable-2">yes</span>) <span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable-2">regexp</span>.<span class="cm-property">test</span>(<span class="cm-variable-2">str</span>)) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`Failure to match '${</span><span class="cm-variable-2">str</span><span class="cm-string-2">}</span><span class="cm-string-2">'`</span>);
}
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">str</span> <span class="cm-keyword">of</span> <span class="cm-variable-2">no</span>) <span class="cm-keyword">if</span> (<span class="cm-variable-2">regexp</span>.<span class="cm-property">test</span>(<span class="cm-variable-2">str</span>)) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`Unexpected match for '${</span><span class="cm-variable-2">str</span><span class="cm-string-2">}</span><span class="cm-string-2">'`</span>);
}
}</pre>
<h3><a class="i_ident" id="i_AFdhUO7dpx" href="#i_AFdhUO7dpx" tabindex="-1" role="presentation"></a>سبک نقل قول کردن</h3>
<p><a class="p_ident" id="p_sHr9S/fcCS" href="#p_sHr9S/fcCS" tabindex="-1" role="presentation"></a>تصور کنید که یک داستان نوشته شده دارید و از علامت نقل قول تکی در طول کتاب برای مشخص کردن دیالوگها استفاده کردهاید. اکنون قصد دارید که همهی علامتهای تکی نقل قول را با علامتهای جفتی عوض کنید و حواستان هم باشد که علامتهای نقل قول تکی که در اختصارهایی مثل <em>aren’t</em> آمدهاند را عوض نکنید.</p>
<p>به الگویی فکر کنید که این دو نوع نقل قول را تمییز دهد و از <code>replace</code> برای جایگزینی صحیح استفاده کنید.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_sPrcOR+s/4" href="#c_sPrcOR+s/4" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">text</span> <span class="cm-operator">=</span> <span class="cm-string">"'I'm the cook,' he said, 'it's my job.'"</span>;
<span class="cm-comment">// Change this call.</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">text</span>.<span class="cm-property">replace</span>(<span class="cm-string-2">/A/g</span>, <span class="cm-string">"B"</span>));
<span class="cm-comment">// → "I'm the cook," he said, "it's my job."</span></pre>
<div class="solution"><div class="solution-text">
<p>روشنترین راه حل برای این مسئله این است که فقط نقلقولهایی را جایگزین کنید که حداقل در یک سمت آن یک غیرکلمه قرار داشته باشد مثل <bdo><code>/\W'|'\W/</code></bdo>. اما همچنین لازم است تا شروع و پایان خط را هم در نظر داشته باشید.</p>
<p>علاوه بر این، باید اطمینان حاصل کنید که جایگزینی شامل کاراکترهایی که توسط <bdo><code>\W</code></bdo> تطبیق میخورند هم باشد تا از قلم نیفتند. این کار را میتوان با قرار دادن آنها درون پرانتز و استفاده از گروههایشان در رشتهی جایگزینی <bdo>(<code>$1</code>, <code>$2</code>)</bdo> انجام داد. گروههایی که تطبیق نمی خورند با چیزی جایگزین نمیشوند.</p>
</div></div>
<h3><a class="i_ident" id="i_pjHcFZTbkd" href="#i_pjHcFZTbkd" tabindex="-1" role="presentation"></a>دوباره اعداد</h3>
<p>عبارتی بنویسید که فقط اعداد سبک جاوااسکریپت را تطبیق دهد. عبارت باید علامت منفی یا مثبت را در جلوی عدد به صورت اختیاری پشتیبانی کند، همچنین نقطهی ممیز و نماد توان - 5 <bdo><code>5e-3</code></bdo> یا <bdo><code>1E10</code></bdo> - را دوباره با علامت اختیاری جلوی توان پشتیبانی کند. همچنین توجه داشته باشید که لازم نیست که بعد از نقطهی ممیز حتما رقم بیاید اما نباید عدد فقط شامل یک نقطهی تنها باشد. بنابراین<bdo><code>.5</code></bdo> و <bdo><code>5.</code></bdo> اعدادی معتبر در جاوااسکریپت محسوب میشوند اما یک نقطهی تنها <em>این طور نیست</em>.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_aHAzeMYYGe" href="#c_aHAzeMYYGe" tabindex="-1" role="presentation"></a><span class="cm-comment">// Fill in this regular expression.</span>
<span class="cm-keyword">let</span> <span class="cm-def">number</span> <span class="cm-operator">=</span> <span class="cm-string-2">/^...$/</span>;
<span class="cm-comment">// Tests:</span>
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">str</span> <span class="cm-keyword">of</span> [<span class="cm-string">"1"</span>, <span class="cm-string">"-1"</span>, <span class="cm-string">"+15"</span>, <span class="cm-string">"1.55"</span>, <span class="cm-string">".5"</span>, <span class="cm-string">"5."</span>,
<span class="cm-string">"1.3e2"</span>, <span class="cm-string">"1E-4"</span>, <span class="cm-string">"1e+12"</span>]) {
<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">number</span>.<span class="cm-property">test</span>(<span class="cm-variable">str</span>)) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`Failed to match '${</span><span class="cm-variable">str</span><span class="cm-string-2">}</span><span class="cm-string-2">'`</span>);
}
}
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">str</span> <span class="cm-keyword">of</span> [<span class="cm-string">"1a"</span>, <span class="cm-string">"+-1"</span>, <span class="cm-string">"1.2.3"</span>, <span class="cm-string">"1+1"</span>, <span class="cm-string">"1e4.5"</span>,
<span class="cm-string">".5."</span>, <span class="cm-string">"1f5"</span>, <span class="cm-string">"."</span>]) {
<span class="cm-keyword">if</span> (<span class="cm-variable">number</span>.<span class="cm-property">test</span>(<span class="cm-variable">str</span>)) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`Incorrectly accepted '${</span><span class="cm-variable">str</span><span class="cm-string-2">}</span><span class="cm-string-2">'`</span>);
}
}</pre>
<div class="solution"><div class="solution-text">
<p>ابتدا، فراموش نکنید که بکاسلش را در جلوی نقطه قرار دهید.</p>
<p>تطبیق علامت اختیاری در جلوی یک عدد، همچنین جلوی یک توان، را میتوان با استفاده از <bdo><code>[+\-]?</code></bdo> یا <bdo><code>(\+|-|)</code></bdo> انجام داد. (مثبت، منفی یا هیچی)</p>
<p>بخش پیچیدهتر این تمرین این است که چهطور هر دوی <bdo><code>"5."</code></bdo> و <bdo><code>".5"</code></bdo> را بدون تطبیق خوردن ".” تطبیق بزنید. برای اینکار، یک راه خوب این است که از | برای جداسازی دو حالت استفاده شود - یک یا دو رقم که ممکن است با یک نقطه و صفر یا ارقام بیشتر ادامه یابد یا نقطهای که به همراه یک را چندین رقم بیاید.</p>
<p><a class="p_ident" id="p_WOazpBSh4J" href="#p_WOazpBSh4J" tabindex="-1" role="presentation"></a>سرانجام، برای اینکه <em>e</em> را غیرحساس به بزرگی/کوچکی حروف داشته باشید، اضافه کردن گزینهی <code>i</code> به انتهای عبارت باقاعده یا استفاده از <code>[eE]</code> مشکل را حل خواهد کرد.</p>
</div></div><nav><a href="08_error.html" title="previous chapter">◀</a> <a href="index.html" title="cover">◆</a> <a href="10_modules.html" title="next chapter">▶</a></nav>
</article>