-
Notifications
You must be signed in to change notification settings - Fork 226
/
Copy pathre-introduction.html
194 lines (165 loc) · 25.5 KB
/
re-introduction.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
<!doctypehtml><html class="sidebar-visible no-js light"lang=en><head><meta charset=UTF-8><title>re introduction - Understanding Python re(gex)?</title><meta content="text/html; charset=utf-8"http-equiv=Content-Type><meta content="Example based guide to mastering Python regular expressions"name=description><meta content=width=device-width,initial-scale=1 name=viewport><meta content=#ffffff name=theme-color><meta content="Understanding Python re(gex)?"property=og:title><meta content=website property=og:type><meta content="Example based guide to mastering Python regular expressions"property=og:description><meta content=https://learnbyexample.github.io/py_regular_expressions/ property=og:url><meta content=https://raw.githubusercontent.com/learnbyexample/py_regular_expressions/master/images/py_regex_ls.png property=og:image><meta content=1280 property=og:image:width><meta content=720 property=og:image:height><meta content=summary_large_image property=twitter:card><meta content=@learn_byexample property=twitter:site><link href=favicon.svg rel=icon><link rel="shortcut icon"href=favicon.png><link href=css/variables.css rel=stylesheet><link href=css/general.css rel=stylesheet><link href=css/chrome.css rel=stylesheet><link href=FontAwesome/css/font-awesome.css rel=stylesheet><link href=fonts/fonts.css rel=stylesheet><link href=highlight.css rel=stylesheet><link href=tomorrow-night.css rel=stylesheet><link href=ayu-highlight.css rel=stylesheet><link href=style.css rel=stylesheet><body><script>var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";</script><script>try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }</script><script>var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');</script><script>var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);</script><nav aria-label="Table of contents"class=sidebar id=sidebar><div class=sidebar-scrollbox><ol class=chapter><li class="chapter-item expanded affix"><a href=cover.html>Cover</a><li class="chapter-item expanded affix"><a href=buy.html>Buy PDF/EPUB versions</a><li class="chapter-item expanded"><a href=preface.html><strong aria-hidden=true>1.</strong> Preface</a><li class="chapter-item expanded"><a href=why-is-it-needed.html><strong aria-hidden=true>2.</strong> Why is it needed?</a><li class="chapter-item expanded"><a class=active href=re-introduction.html><strong aria-hidden=true>3.</strong> re introduction</a><li class="chapter-item expanded"><a href=anchors.html><strong aria-hidden=true>4.</strong> Anchors</a><li class="chapter-item expanded"><a href=alternation-and-grouping.html><strong aria-hidden=true>5.</strong> Alternation and Grouping</a><li class="chapter-item expanded"><a href=escaping-metacharacters.html><strong aria-hidden=true>6.</strong> Escaping metacharacters</a><li class="chapter-item expanded"><a href=dot-metacharacter-and-quantifiers.html><strong aria-hidden=true>7.</strong> Dot metacharacter and Quantifiers</a><li class="chapter-item expanded"><a href=interlude-tools-for-debugging-and-visualization.html><strong aria-hidden=true>8.</strong> Interlude: Tools for debugging and visualization</a><li class="chapter-item expanded"><a href=working-with-matched-portions.html><strong aria-hidden=true>9.</strong> Working with matched portions</a><li class="chapter-item expanded"><a href=character-class.html><strong aria-hidden=true>10.</strong> Character class</a><li class="chapter-item expanded"><a href=groupings-and-backreferences.html><strong aria-hidden=true>11.</strong> Groupings and backreferences</a><li class="chapter-item expanded"><a href=interlude-common-tasks.html><strong aria-hidden=true>12.</strong> Interlude: Common tasks</a><li class="chapter-item expanded"><a href=lookarounds.html><strong aria-hidden=true>13.</strong> Lookarounds</a><li class="chapter-item expanded"><a href=flags.html><strong aria-hidden=true>14.</strong> Flags</a><li class="chapter-item expanded"><a href=unicode.html><strong aria-hidden=true>15.</strong> Unicode</a><li class="chapter-item expanded"><a href=regex-module.html><strong aria-hidden=true>16.</strong> regex module</a><li class="chapter-item expanded"><a href=gotchas.html><strong aria-hidden=true>17.</strong> Gotchas</a><li class="chapter-item expanded"><a href=further-reading.html><strong aria-hidden=true>18.</strong> Further Reading</a><li class="chapter-item expanded"><a href=Exercise_solutions.html><strong aria-hidden=true>19.</strong> Exercise Solutions</a></li><br><hr><li class="chapter-item expanded"><i class="fa fa-github"id=git-repository-button></i><a href=https://github.com/learnbyexample/py_regular_expressions> Source code</a><li class="chapter-item expanded"><i class="fa fa-home"id=home-button></i><a href=https://learnbyexample.github.io/> My Blog</a><li class="chapter-item expanded"><i class="fa fa-book"id=book-button></i><a href=https://learnbyexample.github.io/books/> My Books</a><li class="chapter-item expanded"><i class="fa fa-envelope"id=mail-button></i><a href=https://learnbyexample.gumroad.com/l/learnbyexample-weekly> learnbyexample weekly</a><li class="chapter-item expanded"><i class="fa fa-twitter"id=twitter-button></i><a href=https://twitter.com/learn_byexample> Twitter</a></ol></div><div class=sidebar-resize-handle id=sidebar-resize-handle></div></nav><div class=page-wrapper id=page-wrapper><div class=page><div id=menu-bar-hover-placeholder></div><div class="menu-bar sticky bordered"id=menu-bar><div class=left-buttons><button aria-label="Toggle Table of Contents"title="Toggle Table of Contents"aria-controls=sidebar class=icon-button id=sidebar-toggle type=button><i class="fa fa-bars"></i></button><button aria-label="Change theme"title="Change theme"aria-controls=theme-list aria-expanded=false aria-haspopup=true class=icon-button id=theme-toggle type=button><i class="fa fa-paint-brush"></i></button><ul aria-label=Themes class=theme-popup id=theme-list role=menu><li role=none><button class=theme id=light role=menuitem>Light (default)</button><li role=none><button class=theme id=rust role=menuitem>Rust</button><li role=none><button class=theme id=coal role=menuitem>Coal</button><li role=none><button class=theme id=navy role=menuitem>Navy</button><li role=none><button class=theme id=ayu role=menuitem>Ayu</button></ul><button aria-label="Toggle Searchbar"title="Search. (Shortkey: s)"aria-controls=searchbar aria-expanded=false aria-keyshortcuts=S class=icon-button id=search-toggle type=button><i class="fa fa-search"></i></button></div><h1 class=menu-title>Understanding Python re(gex)?</h1><div class=right-buttons><a aria-label=Blog href=https://learnbyexample.github.io title=Blog> <i class="fa fa-home"id=home-button></i> </a><a aria-label=Twitter href=https://twitter.com/learn_byexample title=Twitter> <i class="fa fa-twitter"id=twitter-button></i> </a><a aria-label="Git repository"title="Git repository"href=https://github.com/learnbyexample/py_regular_expressions> <i class="fa fa-github"id=git-repository-button></i> </a></div></div><div class=hidden id=search-wrapper><form class=searchbar-outer id=searchbar-outer><input placeholder="Search this book ..."aria-controls=searchresults-outer aria-describedby=searchresults-header id=searchbar name=searchbar type=search></form><div class="searchresults-outer hidden"id=searchresults-outer><div class=searchresults-header id=searchresults-header></div><ul id=searchresults></ul></div></div><script>document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});</script><div class=content id=content><main><div class=sidetoc><nav class=pagetoc></nav></div><h1 id=re-introduction><a class=header href=#re-introduction>re introduction</a></h1><p>This chapter gives an introduction to the <code>re</code> module. This module is part of the standard library. For some examples, the equivalent normal string method is also shown for comparison. This chapter just focuses on the basics of using functions from the <code>re</code> module. Regular expression features will be covered from the next chapter onwards.<h2 id=re-module-documentation><a class=header href=#re-module-documentation>re module documentation</a></h2><p>It is always a good idea to know where to find the documentation. The default offering for Python regular expressions is the <code>re</code> standard library module. Visit <a href=https://docs.python.org/3/library/re.html>docs.python: re</a> for information on available methods, syntax, features, examples and more. Here's a quote:<blockquote><p>A regular expression (or RE) specifies a set of strings that matches it; the functions in this module let you check if a particular string matches a given regular expression</blockquote><h2 id=research><a class=header href=#research>re.search()</a></h2><p>Normally you'd use the <code>in</code> operator to test whether a string is part of another string or not. For regular expressions, use the <code>re.search()</code> function whose argument list is shown below.<blockquote><p><code>re.search(pattern, string, flags=0)</code></blockquote><p>The first argument is the RE pattern you want to test against the input string, which is the second argument. <code>flags</code> is optional, it helps to change the default behavior of RE patterns.<p>As a good practice, always use <strong>raw strings</strong> to construct the RE pattern. This will become clearer in later chapters. Here are some examples to get started.<pre><code class=language-python>>>> sentence = 'This is a sample string'
# check if 'sentence' contains the given search string
>>> 'is' in sentence
True
>>> 'xyz' in sentence
False
# need to load the re module before use
>>> import re
# check if 'sentence' contains the pattern described by the RE argument
>>> bool(re.search(r'is', sentence))
True
>>> bool(re.search(r'xyz', sentence))
False
</code></pre><p>Before using the <code>re</code> module, you need to <code>import</code> it. Further example snippets will assume that this module is already loaded. The return value of the <code>re.search()</code> function is a <code>re.Match</code> object when a match is found and <code>None</code> otherwise (note that I treat <code>re</code> as a word, not as <code>r</code> and <code>e</code> separately, hence the use of <em>a</em> instead of <em>an</em>). More details about the <code>re.Match</code> object will be discussed in the <a href=./working-with-matched-portions.html#working-with-matched-portions>Working with matched portions</a> chapter. For presentation purposes, the examples will use the <code>bool()</code> function to show <code>True</code> or <code>False</code> depending on whether the RE pattern matched or not.<p>Here's an example with the <code>flags</code> optional argument. By default, the pattern will match the input string case sensitively. By using the <code>re.I</code> flag, you can match case insensitively. See the <a href=./flags.html#flags>Flags</a> chapter for more details.<pre><code class=language-python>>>> sentence = 'This is a sample string'
>>> bool(re.search(r'this', sentence))
False
# re.IGNORECASE (or re.I) is a flag to enable case insensitive matching
>>> bool(re.search(r'this', sentence, flags=re.I))
True
</code></pre><h2 id=research-in-conditional-expressions><a class=header href=#research-in-conditional-expressions>re.search() in conditional expressions</a></h2><p>As Python evaluates <code>None</code> as <code>False</code> in boolean context, <code>re.search()</code> can be used directly in conditional expressions. See also <a href=https://docs.python.org/3/library/stdtypes.html#truth>docs.python: Truth Value Testing</a>.<pre><code class=language-python>>>> sentence = 'This is a sample string'
>>> if re.search(r'ring', sentence):
... print('mission success')
...
mission success
>>> if not re.search(r'xyz', sentence):
... print('mission failed')
...
mission failed
</code></pre><p>Here are some examples with list comprehensions and generator expressions:<pre><code class=language-python>>>> words = ['cat', 'attempt', 'tattle']
>>> [w for w in words if re.search(r'tt', w)]
['attempt', 'tattle']
>>> all(re.search(r'at', w) for w in words)
True
>>> any(re.search(r'stat', w) for w in words)
False
</code></pre><h2 id=resub><a class=header href=#resub>re.sub()</a></h2><p>For normal search and replace, you'd use the <code>str.replace()</code> method. For regular expressions, use the <code>re.sub()</code> function, whose argument list is shown below.<blockquote><p><code>re.sub(pattern, repl, string, count=0, flags=0)</code></blockquote><p>The first argument is the RE pattern to match against the input string, which is the third argument. The second argument specifies the string which will replace the portions matched by the RE pattern. <code>count</code> and <code>flags</code> are optional arguments.<pre><code class=language-python>>>> greeting = 'Have a nice weekend'
# replace all occurrences of 'e' with 'E'
# same as: greeting.replace('e', 'E')
>>> re.sub(r'e', 'E', greeting)
'HavE a nicE wEEkEnd'
# replace the first two occurrences of 'e' with 'E'
# same as: greeting.replace('e', 'E', count=2)
>>> re.sub(r'e', 'E', greeting, count=2)
'HavE a nicE weekend'
</code></pre><blockquote><p><img alt=warning src=images/warning.svg> A common mistake, not specific to <code>re.sub()</code>, is forgetting that strings are immutable in Python.<pre><code class=language-python>>>> word = 'cater'
# this will return a string object, won't modify the 'word' variable
>>> re.sub(r'cat', 'wag', word)
'wager'
>>> word
'cater'
# need to explicitly assign the result if 'word' has to be changed
>>> word = re.sub(r'cat', 'wag', word)
>>> word
'wager'
</code></pre></blockquote><h2 id=compiling-regular-expressions><a class=header href=#compiling-regular-expressions>Compiling regular expressions</a></h2><p>Regular expressions can be compiled using the <code>re.compile()</code> function, which gives back a <code>re.Pattern</code> object.<blockquote><p><code>re.compile(pattern, flags=0)</code></blockquote><p>The top level <code>re</code> module functions are all available as methods for such objects. Compiling a regular expression is useful if the RE has to be used in multiple places or called upon multiple times inside a loop (speed benefit).<blockquote><p><img alt=info src=images/info.svg> By default, Python maintains a small list of recently used RE, so the speed benefit doesn't apply for trivial use cases. See also <a href=https://stackoverflow.com/q/452104/4082052>stackoverflow: Is it worth using re.compile?</a></blockquote><pre><code class=language-python>>>> pet = re.compile(r'dog')
>>> type(pet)
<class 're.Pattern'>
# note that 'search' is called upon 'pet' which is a 're.Pattern' object
# since 'pet' has the RE information, you only need to pass the input string
>>> bool(pet.search('They bought a dog'))
True
>>> bool(pet.search('A cat crossed their path'))
False
# replace all occurrences of 'dog' with 'cat'
>>> pet.sub('cat', 'They bought a dog')
'They bought a cat'
</code></pre><p>Some of the methods available for compiled patterns also accept more arguments than those available for the top level functions of the <code>re</code> module. For example, the <code>search()</code> method on a compiled pattern has two optional arguments to specify the <strong>start</strong> and <strong>end</strong> index positions. Similar to the <code>range()</code> function and slicing notation, the ending index has to be specified <code>1</code> greater than the desired index.<blockquote><p><code>Pattern.search(string[, pos[, endpos]])</code></blockquote><p>Note that there's no <code>flags</code> option as that has to be specified with <code>re.compile()</code>.<pre><code class=language-python>>>> sentence = 'This is a sample string'
>>> word = re.compile(r'is')
# search for 'is' starting from the 5th character
>>> bool(word.search(sentence, 4))
True
# search for 'is' starting from the 7th character
>>> bool(word.search(sentence, 6))
False
# search for 'is' from the 3rd character to the 4th character
>>> bool(word.search(sentence, 2, 4))
True
</code></pre><h2 id=bytes><a class=header href=#bytes>bytes</a></h2><p>To work with the <code>bytes</code> data type, the RE must be specified as <code>bytes</code> as well. Similar to the <code>str</code> RE, use <strong>raw</strong> format to construct a <code>bytes</code> RE.<pre><code class=language-python>>>> byte_data = b'This is a sample string'
# error message truncated for presentation purposes
>>> re.search(r'is', byte_data)
TypeError: cannot use a string pattern on a bytes-like object
# use rb'..' for constructing bytes pattern
>>> bool(re.search(rb'is', byte_data))
True
>>> bool(re.search(rb'xyz', byte_data))
False
</code></pre><h2 id=regex-playground><a class=header href=#regex-playground>re(gex)? playground</a></h2><p>To make it easier to experiment, I wrote an interactive TUI app. See <a href=https://github.com/learnbyexample/TUI-apps/tree/main/PyRegexPlayground>PyRegexPlayground</a> repo for installation instructions and usage guide. A sample screenshot is shown below:<p align=center><img alt="Python re(gex)? playground"src=images/pyregex_playground.png><h2 id=cheatsheet-and-summary><a class=header href=#cheatsheet-and-summary>Cheatsheet and Summary</a></h2><div class=table-wrapper><table><thead><tr><th>Note<th>Description<tbody><tr><td><a href=https://docs.python.org/3/library/re.html>docs.python: re</a><td>Python standard module for regular expressions<tr><td><code>re.search()</code><td>Check if the given pattern is present anywhere in the input string<tr><td><td><code>re.search(pattern, string, flags=0)</code><tr><td><td>Output is a <code>re.Match</code> object, usable in conditional expressions<tr><td><td>raw strings preferred to define RE<tr><td><td>Additionally, Python maintains a small cache of recent RE<tr><td><code>re.sub()</code><td>search and replace using RE<tr><td><td><code>re.sub(pattern, repl, string, count=0, flags=0)</code><tr><td><code>re.compile()</code><td>Compile a pattern for reuse, output is a <code>re.Pattern</code> object<tr><td><td><code>re.compile(pattern, flags=0)</code><tr><td><code>rb'pat'</code><td>Use byte pattern for byte input<tr><td><code>re.IGNORECASE</code> or <code>re.I</code><td>flag to ignore case while matching</table></div><p>This chapter introduced the <code>re</code> module, which is part of the standard library. Functions <code>re.search()</code> and <code>re.sub()</code> were discussed as well as how to compile RE using the <code>re.compile()</code> function. The RE pattern is usually defined using raw strings. For byte input, the pattern has to be of byte type too. Although the <code>re</code> module is good enough for most use cases, there are situations where you need to use the third-party <code>regex</code> module. To avoid mixing up features, a separate chapter is dedicated for the <a href=./regex-module.html#regex-module>regex module</a> at the end of this book.<p>The next section has exercises to test your understanding of the concepts introduced in this chapter. Please do solve them before moving on to the next chapter.<h2 id=exercises><a class=header href=#exercises>Exercises</a></h2><blockquote><p><img alt=info src=images/info.svg> Try to solve exercises in every chapter using only the features discussed until that chapter. Some of the exercises will be easier to solve with techniques presented in later chapters, but the aim of these exercises is to explore the features presented so far.</blockquote><blockquote><p><img alt=info src=images/info.svg> All the exercises are also collated together in one place at <a href=https://github.com/learnbyexample/py_regular_expressions/blob/master/exercises/Exercises.md>Exercises.md</a>. For solutions, see <a href=https://github.com/learnbyexample/py_regular_expressions/blob/master/exercises/Exercise_solutions.md>Exercise_solutions.md</a>.</blockquote><p><strong>1)</strong> Check whether the given strings contain <code>0xB0</code>. Display a boolean result as shown below.<pre><code class=language-python>>>> line1 = 'start address: 0xA0, func1 address: 0xC0'
>>> line2 = 'end address: 0xFF, func2 address: 0xB0'
>>> bool(re.search(r'', line1)) ##### add your solution here
False
>>> bool(re.search(r'', line2)) ##### add your solution here
True
</code></pre><p><strong>2)</strong> Replace all occurrences of <code>5</code> with <code>five</code> for the given string.<pre><code class=language-python>>>> ip = 'They ate 5 apples and 5 oranges'
>>> re.sub() ##### add your solution here
'They ate five apples and five oranges'
</code></pre><p><strong>3)</strong> Replace only the first occurrence of <code>5</code> with <code>five</code> for the given string.<pre><code class=language-python>>>> ip = 'They ate 5 apples and 5 oranges'
>>> re.sub() ##### add your solution here
'They ate five apples and 5 oranges'
</code></pre><p><strong>4)</strong> For the given list, filter all elements that do <em>not</em> contain <code>e</code>.<pre><code class=language-python>>>> items = ['goal', 'new', 'user', 'sit', 'eat', 'dinner']
>>> [w for w in items if not re.search()] ##### add your solution here
['goal', 'sit']
</code></pre><p><strong>5)</strong> Replace all occurrences of <code>note</code> irrespective of case with <code>X</code>.<pre><code class=language-python>>>> ip = 'This note should not be NoTeD'
>>> re.sub() ##### add your solution here
'This X should not be XD'
</code></pre><p><strong>6)</strong> Check if <code>at</code> is present in the given byte input data.<pre><code class=language-python>>>> ip = b'tiger imp goat'
>>> bool(re.search()) ##### add your solution here
True
</code></pre><p><strong>7)</strong> For the given input string, display all lines not containing <code>start</code> irrespective of case.<pre><code class=language-python>>>> para = '''good start
... Start working on that
... project you always wanted
... stars are shining brightly
... hi there
... start and try to
... finish the book
... bye'''
>>> pat = re.compile() ##### add your solution here
>>> for line in para.split('\n'):
... if not pat.search(line):
... print(line)
...
project you always wanted
stars are shining brightly
hi there
finish the book
bye
</code></pre><p><strong>8)</strong> For the given list, filter all elements that contain either <code>a</code> or <code>w</code>.<pre><code class=language-python>>>> items = ['goal', 'new', 'user', 'sit', 'eat', 'dinner']
##### add your solution here
>>> [w for w in items if re.search() or re.search()]
['goal', 'new', 'eat']
</code></pre><p><strong>9)</strong> For the given list, filter all elements that contain both <code>e</code> and <code>n</code>.<pre><code class=language-python>>>> items = ['goal', 'new', 'user', 'sit', 'eat', 'dinner']
##### add your solution here
>>> [w for w in items if re.search() and re.search()]
['new', 'dinner']
</code></pre><p><strong>10)</strong> For the given string, replace <code>0xA0</code> with <code>0x7F</code> and <code>0xC0</code> with <code>0x1F</code>.<pre><code class=language-python>>>> ip = 'start address: 0xA0, func1 address: 0xC0'
##### add your solution here
'start address: 0x7F, func1 address: 0x1F'
</code></pre></main><nav aria-label="Page navigation"class=nav-wrapper><a aria-label="Previous chapter"class="mobile-nav-chapters previous"title="Previous chapter"aria-keyshortcuts=Left href=why-is-it-needed.html rel=prev> <i class="fa fa-angle-left"></i> </a><a aria-label="Next chapter"class="mobile-nav-chapters next"title="Next chapter"aria-keyshortcuts=Right href=anchors.html rel=next> <i class="fa fa-angle-right"></i> </a><div style="clear: both"></div></nav></div></div><nav aria-label="Page navigation"class=nav-wide-wrapper><a aria-label="Previous chapter"class="nav-chapters previous"title="Previous chapter"aria-keyshortcuts=Left href=why-is-it-needed.html rel=prev> <i class="fa fa-angle-left"></i> </a><a aria-label="Next chapter"class="nav-chapters next"title="Next chapter"aria-keyshortcuts=Right href=anchors.html rel=next> <i class="fa fa-angle-right"></i> </a></nav></div><script>window.playground_copyable = true;</script><script charset=utf-8 src=elasticlunr.min.js></script><script charset=utf-8 src=mark.min.js></script><script charset=utf-8 src=searcher.js></script><script charset=utf-8 src=clipboard.min.js></script><script charset=utf-8 src=highlight.js></script><script charset=utf-8 src=book.js></script><script src=sidebar.js></script>