-
Notifications
You must be signed in to change notification settings - Fork 41
/
15-regular-expressions.Rmd
726 lines (544 loc) · 21.8 KB
/
15-regular-expressions.Rmd
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
# Regular expressions
**Learning objectives:**
- Manipulate **character vectors** using simple `{stringr}` functions.
- Use **regular expressions (regex)** to match **patterns** in strings.
- Use `{stringr}` **functions** and **regex** to **manipulate strings.**
- Use **other {stringr} matching rules** to find specific patterns.
## Introduction {-}
> This chapter is going to be wicked fun!!!
- Regexs make up the backbone of textual searches.
- "But can't I just `ctrl + f` and search my document? Well, yes...but no...
- Regular Expressions translate your search into computer code.
[stringr pkgdown site](https://stringr.tidyverse.org/)
[stringr cheatsheet](https://github.com/rstudio/cheatsheets/blob/main/strings.pdf)
### Pre-Requisites {-}
We will be utilizing the `{stringr}` package which is part of the core `{tidyverse}` and also the package `{babynames}`.
```{r 17-Load Library, include = TRUE, message = FALSE}
library(tidyverse)
library(babynames)
```
## Why regexp? Google Sheets find and replace {-}
![Google Sheets find and replace](images/15_search_google_sheets_regex.png)
## Why regexp? IPv4 loop {-}
```{r}
is_valid_ipv4_loop <- function(ip) {
octets <- strsplit(ip, "\\.")[[1]]
if (length(octets) != 4) {
# print("number of octets is not equal to 4")
return(FALSE)
}
for (octet in octets) {
if (!all(as.numeric(octet) >= 0, as.numeric(octet) <= 255)) {
# print(as.numeric(octet))
return(FALSE)
}
}
TRUE
}
ip1 <- "192.168.1.1"
ip2 <- "256.0.0.1"
ip3 <- "192.168.001.001"
is_valid_ipv4_loop(ip1)
is_valid_ipv4_loop(ip2)
is_valid_ipv4_loop(ip3)
```
## Why regexp? IPv4 regexp {-}
```{r}
is_valid_ipv4_regex <- function(ip) {
pattern <- "^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})(\\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})){3}$"
grepl(pattern, ip)
}
is_valid_ipv4_regex(ip1)
is_valid_ipv4_regex(ip2)
is_valid_ipv4_regex(ip3)
```
## Why regexp? IPv4 regexplained {-}
`pattern <- "^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})(\\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})){3}$"`
1. `^`: Anchors the regex at the beginning of the string.
2. `(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})`: This part represents a single octet in the range of 0 to 255.
- `25[0-5]`: Matches 250 to 255.
- `2[0-4][0-9]`: Matches 200 to 249.
- `[0-1]?[0-9]{1,2}`: Matches 0 to 199. `[0-1]?` allows for an optional leading 0 or 1, and `[0-9]{1,2}` matches 1 or 2 digits.
3. `(\\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})){3}`: This part represents the remaining three octets, each separated by a dot.
4. `$`: Anchors the regex at the end of the string.
In summary, the regular expression ensures that the IP address consists of four octets separated by dots
and each octet is in the valid range of 0 to 255. The `^` and `$` anchors ensure that the entire string is matched, not just a part of it.
Here's how the regex works for an example IP like "192.168.1.1":
- `192`: Matches the first part.
- `.`: Matches the dot separator.
- `168`: Matches the second part.
- `.`: Matches the dot separator.
- `1`: Matches the third part.
- `.`: Matches the dot separator.
- `1`: Matches the fourth part.
- `$`: Ensures the end of the string.
If the string doesn't match this pattern, `grepl` returns `FALSE`, indicating that the IP is not valid according to the IPv4 format.
## Matching Patterns with Regular Expressions {-}
- `str_view()`: show the elements of the string vector that match, surrounding each match with `<>`
- [RegExplain](https://www.garrickadenbuie.com/project/regexplain/): RStudio addin/utility belt for regular expressions
- [regexr](https://regexr.com/): online tool to learn, build, & test Regular Expressions
## Exact match letters numbers {-}
The simplest expressions are pattern matching:
```{r 17-Pattern Matching, include=TRUE}
str_view(fruit, "berry")
```
## Metacharacters {-}
- `.`, `+`, `*`, `[`, `]`, and `?` are metacharacters and have special meanings.
- `.` matches any character (except a newline):
```{r 17-Match any Character, include = TRUE}
# any string that contains an “a” followed by another character
str_view(c("a", "ab", "ae", "bd", "ea", "eab"), "a.")
# all the fruits that contain an “a”, followed by three letters,
# followed by an “e”:
str_view(fruit, "a...e")
```
## Quantifiers {-}
Quantifiers control how many times a pattern can match:
- `?` makes a pattern optional (i.e. it matches 0 or 1 times)
- `+` lets a pattern repeat (i.e. it matches at least once)
- `*` lets a pattern be optional or repeat (i.e. it matches any number of times, including 0).
```{r}
# ab? matches an "a", optionally followed by a "b".
str_view(c("a", "ab", "abb"), "ab?")
# ab+ matches an "a", followed by at least one "b".
str_view(c("a", "ab", "abb"), "ab+")
# ab* matches an "a", followed by any number of "b"s.
str_view(c("a", "ab", "abb"), "ab*")
```
## Character classes {-}
- Defined by `[]`: match a set of characters
- `[abcd]` matches “a”, “b”, “c”, or “d”.
- Invert the match: start with `^`:
- `[^abcd]` matches anything except “a”, “b”, “c”, or “d”.
```{r}
# words containing an “x” surrounded by vowels:
str_view(words, "[aeiou]x[aeiou]")
# words containing a “y” surrounded by consonants
str_view(words, "[^aeiou]y[^aeiou]")
```
## Alternation {-}
- Defined by `|`: to pick between one or more alternative patterns.
```{r}
# look for fruits containing “apple”, “melon”, or “nut”
str_view(fruit, "apple|melon|nut")
# look for a repeated vowel
str_view(fruit, "aa|ee|ii|oo|uu")
```
## Detect matches {-}
- `str_detect()`: returns a logical vector that is TRUE if the pattern matches an element of the character vector and FALSE otherwise:
- can be used with `filter()`
```{r}
str_detect(c("a", "b", "c"), "[aeiou]")
# find all the most popular names containing a lower-case “x”
babynames |>
filter(str_detect(name, "x")) |>
count(name, wt = n, sort = TRUE)
```
## Detect matches (2) {-}
- `str_detect()` with `summarize()`:
- sum(str_detect(x, pattern)) returns the number of observations that match
- mean(str_detect(x, pattern)) returns the proportion that match.
```{r}
# compute and visualize the proportion of baby names that contain “x”
# broken down by year
babynames |>
group_by(year) |>
summarize(prop_x = mean(str_detect(name, "x"))) |>
ggplot(aes(x = year, y = prop_x)) +
geom_line()
```
## str_detect() related {-}
- `str_subset()`: returns a character vector containing only the strings that match.
- `str_which()`: returns an integer vector giving the positions of the strings that match.
## Count matches {-}
- `str_count()`: returns how many matches there are in each string.
- each match starts at the end of the previous match, regex matches don't overlap.
```{r}
x <- c("apple", "banana", "pear")
str_count(x, "p")
str_count("abababa", "aba")
str_view("abababa", "aba")
```
## Pitfall: case sensitive regex {-}
Possible fixes:
- Add the upper case vowels to the character class: `str_count(name, "[aeiouAEIOU]")`.
- Ignore case: `str_count(name, regex("[aeiou]", ignore_case = TRUE))`.
- Use `str_to_lower()` to convert the names to lower case.
```{r}
babynames |>
count(name) |>
mutate(
name = str_to_lower(name),
vowels = str_count(name, "[aeiou]"),
consonants = str_count(name, "[^aeiou]")
)
```
## Extract variables {-}
- `separate_wider_regex()`: extract data out of one column into one or more new columns.
- construct a sequence of regular expressions that match each piece.
- give each a name to have the contents of that piece to appear in the output.
```{r}
df <- tribble(
~str,
"<Sheryl>-F_34",
"<Kisha>-F_45",
"<Brandon>-N_33",
"<Sharon>-F_38",
"<Penny>-F_58",
"<Justin>-M_41",
"<Patricia>-F_84",
)
df |>
separate_wider_regex(
str,
patterns = c(
"<",
name = "[A-Za-z]+",
">-",
gender = ".",
"_",
age = "[0-9]+"
)
)
```
## Extract variables debug {-}
If the match fails
- `too_few = "debug"`
```{r}
df <- tribble(
~str,
"<Sheryl>-F_",
"<Kisha>@-F_45",
"<Brandon>-N_33",
"<Sharon>-F_38",
"<Penny>-F_58",
"<Justin>-M_41",
"<Patricia>-F_84",
)
df |>
separate_wider_regex(
str,
patterns = c(
"<",
name = "[A-Za-z]+",
">-",
gender = ".",
"_",
age = "[0-9]+"
),
too_few = "debug"
)
```
## Pattern details {-}
- **escaping** to match metacharacters that would otherwise be treated specially.
- **anchors** to match the start or end of the string.
- **character classes** and their shortcuts to match any character from a set.
- **quantifiers** which control how many times a pattern can match.
- **operator** precedence and parentheses.
- **grouping** components of the pattern.
## Escaping metacharacters {-}
- all of the metacharacters: `.^$\|*+?{}[]()`
- to match a metacharacter you need to escape with `\`
- regexes are represented by strings that also use `\` as escape symbol, so to escape a dot `.` for example, you need to use `"\\."`
```{r}
# To create the regular expression \., we need to use \\.
dot <- "\\."
# But the expression itself only contains one \
str_view(dot)
# And this tells R to look for an explicit .
str_view(c("abc", "a.c", "bef"), "a\\.c")
```
## Escape literal `\` {-}
- `\` is the escape character for strings and regexes
- to escape a `\` you need to write `"\\\\"`
```{r}
x <- "a\\b"
str_view(x)
str_view(x, "\\\\")
```
## Escaping metacharacters, alternatives {-}
- use raw strings (previous chapter) `r"{}"`, `r"[]"` or `r"{}"`
- use character class: `.`, `$`, `|`, `*`, `+`, `?`, `{`, `}`, `(`, `)` can be matched with `[.]`, `[$]`, `[|]`, etc.
```{r}
str_view(x, r"{\\}")
str_view(c("abc", "a.c", "a*c", "a c"), "a[.]c")
str_view(c("abc", "a.c", "a*c", "a c"), ".[*]c")
```
## Character classes {-}
- character class or set `[]`: match any character in a set.
- construct your own sets with `[]`
- `[abc]` matches “a”, “b”, or “c”
- `[^abc]` matches any character except “a”, “b”, or “c”.
- two other characters that have special meaning inside of `[]`:
- `-` defines a range, e.g., [a-z] matches any lower case letter and [0-9] matches any number.
- `\` escapes special characters, so `[\^\-\]]` matches `^`, `-`, or `]`.
```{r}
x <- "abcd ABCD 12345 -!@#%."
str_view(x, "[abc]+")
str_view(x, "[a-z]+")
str_view(x, "[^a-z0-9]+")
# You need an escape to match characters that are otherwise
# special inside of []
str_view("a-b-c", "[a-c]")
str_view("a-b-c", "[a\\-c]")
```
## Special character classes {-}
Commonly used character classes with their own shortcut:
- `\.` matches any character apart from a newline.
- `\d` matches any digit.
- `\D` matches anything that isn’t a digit.
- `\s` matches any whitespace (e.g., space, tab, newline).
- `\S` matches anything that isn’t whitespace.
- `\w` matches any “word” character, i.e. letters and numbers.
- `\W` matches any “non-word” character.
```{r}
x <- "abcd ABCD 12345 -!@#%."
str_view(x, "\\d+")
str_view(x, "\\D+")
str_view(x, "\\s+")
str_view(x, "\\S+")
str_view(x, "\\w+")
str_view(x, "\\W+")
```
## Quantifiers {-}
Quantifiers control how many times a pattern matches.
- `?` 0 or 1 matches.
- `+` 1 or more matches.
- `*` 0 or more matches.
Specify the number of matches with `{}`:
- `{n}` matches exactly n times.
- `{n,}` matches at least n times.
- `{n,m}` matches between n and m times.
## Operator precedence and parentheses {-}
Quantifiers have high precedence and Alternation has low precedence
- `ab+` is equivalent to `a(b+)`
- “a” followed by one or more “b”s
- `^a|b$` is equivalent to `(^a)|(b$)`
- a string starting with "a" or a string ending with "b"
Use parentheses to override the usual order and or improve legibility.
## Grouping and capturing {-}
- Parentheses can be used to create capturing groups.
- Refer back to a match
- `\1` refers back to match of the first parenthesis
- `\2` refers back to match of the second parenthesis, etc
```{r}
#the following pattern finds all fruits that have a repeated pair of letters
str_view(fruit, "(..)\\1")
#this one finds all words that start and end with the same pair of letters:
str_view(words, "^(..).*\\1$")
```
## Back references in str_replace() {-}
```{r}
#switch the order of the second and third words in sentences
sentences |>
str_replace("(\\w+) (\\w+) (\\w+)", "\\1 \\3 \\2") |>
str_view()
```
## Non-capturing group {-}
To use parentheses without creating matching groups, you can create a non-capturing group with `(?:)`.
```{r}
x <- c("a gray cat", "a grey dog")
str_match(x, "gr(e|a)y")
str_match(x, "gr(?:e|a)y")
```
## Regex flags, ignore_case {-}
Settings in `{stringr}` to control the details of the regexp.
`ignore_case = TRUE` allows characters to match either their uppercase or lowercase forms
```{r}
bananas <- c("banana", "Banana", "BANANA")
str_view(bananas, "banana")
str_view(bananas, regex("banana", ignore_case = TRUE))
```
## Regex flags, dotall and multiline {-}
Useful for work with multiline strings (i.e. strings that contain `\n`)
`dotall = TRUE` lets . match everything, including `\n`:
```{r}
x <- "Line 1\nLine 2\nLine 3"
str_view(x, ".Line")
str_view(x, regex(".Line", dotall = TRUE))
```
`multiline = TRUE` makes `^` and `$` match the start and end of each line rather than the start and end of the complete string:
```{r}
x <- "Line 1\nLine 2\nLine 3"
str_view(x, "^Line")
str_view(x, regex("^Line", multiline = TRUE))
```
## Regex flags, comments {-}
- `comments = TRUE` tweaks the pattern language to ignore spaces and new lines, as well as everything after #.
- use comments and whitespace to make complex regular expressions more understandable
- using `comments` and want to match a space, newline, or #, you’ll need to escape it with `\`.
```{r}
phone <- regex(
r"(
\(? # optional opening parens
(\d{3}) # area code
[)\-]? # optional closing parens or dash
\ ? # optional space
(\d{3}) # another three numbers
[\ -]? # optional space or dash
(\d{4}) # four more numbers
)",
comments = TRUE
)
str_extract(c("514-791-8141", "(123) 456 7890", "123456"), phone)
```
## Fixed matches {-}
`fixed()` allows to opt-out of the regular expression rules or to ignore case
```{r}
str_view(c("", "a", "."), fixed("."))
str_view("x X", "X")
str_view("x X", fixed("X", ignore_case = TRUE))
```
## Fixed matches, coll() {-}
- `coll()` instead of `fixed()` for non-English text, as it implements the full rules for capitalization as used by the specified locale.
```{r}
str_view("i İ ı I", fixed("İ", ignore_case = TRUE))
str_view("i İ ı I", coll("İ", ignore_case = TRUE, locale = "tr"))
```
## General techniques {-}
To solve real world problems:
1. Check your work by creating simple positive and negative controls.
2. Combine regular expressions with Boolean algebra.
3. Create complex patterns using string manipulation.
## Meeting Videos {-}
### Cohort 5 {-}
`r knitr::include_url("https://www.youtube.com/embed/RpZoQR2xwME")`
<details>
<summary> Meeting chat log </summary>
```
00:10:10 Njoki Njuki Lucy: Hi everyone!
00:10:24 Federica Gazzelloni: Hello!
00:10:31 Sandra Muroy: hi!
00:11:09 Federica Gazzelloni: Hi Sandra!
00:11:27 Sandra Muroy: Hi Federica :D
00:11:43 Sandra Muroy: Hi Lucy!
00:19:20 Jon Harmon (jonthegeek): https://xkcd.com/208/
00:20:10 Jon Harmon (jonthegeek): https://smltar.com/ is the new book by Julia Silge and Emil Hvitfeldt.
00:20:42 Jon Harmon (jonthegeek): https://www.tidytextmining.com/ is the older Tidytext book by Julia Silge and David Robinson.
00:28:15 Njoki Njuki Lucy: do you also load the packages or that's sufficient for a readable code?
00:28:59 Jon Harmon (jonthegeek): Within an Rmd I tend to put a library() call per package at the top so it's easy to find, but in my exploration work I rarely library anything other than magrittr.
00:30:11 Njoki Njuki Lucy: okay, thank you
00:39:53 Jon Harmon (jonthegeek): > x <- r"(this treats all characters as ecaped. For example: \ " ')"
> writeLines(x)
this treats all characters as ecaped. For example: \ " '
00:45:30 Jon Harmon (jonthegeek): \"
00:45:31 Federica Gazzelloni: textutils: https://github.com/enricoschumann/textutils
00:45:42 Jon Harmon (jonthegeek): "\""
00:46:00 Jon Harmon (jonthegeek): """
00:46:19 Jon Harmon (jonthegeek): "\\"
00:49:07 Jon Harmon (jonthegeek): > "\"
+
00:50:06 Jon Harmon (jonthegeek): > cat("this\ttab")
this tab
00:50:21 Jon Harmon (jonthegeek): > cat("this\nnewline")
this
newline
00:50:40 Jon Harmon (jonthegeek): > cat("this\something"
Error: '\s' is an unrecognized escape in character string starting ""this\s"
00:51:20 Njoki Njuki Lucy: did I get it right that the character after \ is escaped not printed?
00:51:30 Jon Harmon (jonthegeek): correct
00:51:41 Jon Harmon (jonthegeek): https://regexr.com/
00:59:49 Jon Harmon (jonthegeek): > str_c(rep("prefix", 3), c("a", "b", "c"), rep("suffix", 3), sep = "-")
[1] "prefix-a-suffix" "prefix-b-suffix" "prefix-c-suffix"
01:00:25 Jon Harmon (jonthegeek): > str_c("prefix", c("a", "b", "c"), "suffix", sep = "-")
[1] "prefix-a-suffix" "prefix-b-suffix" "prefix-c-suffix"
01:01:26 Jon Harmon (jonthegeek): > str_c(c("prefix", "other", "another"), c("a", "b", "c"), "suffix", sep = "-")
[1] "prefix-a-suffix" "other-b-suffix" "another-c-suffix"
01:02:02 Jon Harmon (jonthegeek): > str_c(c("prefix", "other"), c("a", "b", "c"), "suffix", sep = "-")
[1] "prefix-a-suffix" "other-b-suffix" "prefix-c-suffix"
Warning message:
In stri_c(..., sep = sep, collapse = collapse, ignore_null = TRUE) :
longer object length is not a multiple of shorter object length
01:04:54 Jon Harmon (jonthegeek): > str_c("prefix", c("a", "b", "c"), "suffix", sep = "-", collapse = "|")
[1] "prefix-a-suffix|prefix-b-suffix|prefix-c-suffix"
01:09:51 Njoki Njuki Lucy: thank you so much Ryan and Jon, strings are starting to make sense now!
01:09:53 Sandra Muroy: thanks so much Ryna!
01:09:56 Sandra Muroy: Ryan!
```
</details>
`r knitr::include_url("https://www.youtube.com/embed/A-_URHkvxNA")`
<details>
<summary> Meeting chat log </summary>
```
00:07:40 Jon Harmon (jonthegeek): statlearning.com
00:23:39 Jon Harmon (jonthegeek): ^ = start of text
$ = end of text
00:31:17 Jon Harmon (jonthegeek): Notes are finally updated on our site if you want to follow along or review: https://r4ds.github.io/bookclub-r4ds/strings.html
00:31:37 Sandra Muroy: thanks Jon!
00:37:56 Jon Harmon (jonthegeek): https://en.wikipedia.org/wiki/Harvard_sentences
00:42:35 Jon Harmon (jonthegeek): sum(stringr::str_detect(sentences, str_c(words, collapse = "|"), negate = TRUE))
00:45:50 Jon Harmon (jonthegeek): 1 %in% 1:10
00:46:02 Jon Harmon (jonthegeek): "a" %in% "ab"
00:46:08 Jon Harmon (jonthegeek): "a"
00:46:10 Jon Harmon (jonthegeek): "ab"
00:46:18 Jon Harmon (jonthegeek): "a" %in% c("a", "b")
01:02:39 Jon Harmon (jonthegeek): regexr.com
```
</details>
### Cohort 6 {-}
`r knitr::include_url("https://www.youtube.com/embed/l07Vo1wMW5s")`
<details>
<summary> Meeting chat log </summary>
```
00:09:53 Daniel Adereti: We will kick off in 1 min
00:14:55 Shannon: https://r4ds.github.io/bookclub-r4ds/string-basics.html
00:18:48 Shannon: https://stringr.tidyverse.org/
00:19:06 Shannon: https://github.com/rstudio/cheatsheets/blob/main/strings.pdf
00:21:02 Daniel Adereti: For new members, we have this sheet where we volunteer to take classes each week, you are welcome to fill in your names. Thanks! https://docs.google.com/spreadsheets/d/1zy2nXNkvcdqWuF8rQ5ApWRkVQG_UJt0azu3h_mEnY2E/edit?usp=drive_web&ouid=115626840160043101981
00:26:49 Shannon: https://en.wikipedia.org/wiki/List_of_Unicode_characters
01:05:02 Daniel Adereti: That's fine!
01:05:13 Daniel Adereti: We can pick up next week
01:05:37 Adeyemi Olusola: Thank you!
01:05:45 Folashade Bayode: Thank you 😊
```
</details>
`r knitr::include_url("https://www.youtube.com/embed/k7poFr556To")`
<details>
<summary> Meeting chat log </summary>
```
00:15:15 Daniel Adereti: We have this nice book that solves the exercises, if you are interested: https://jrnold.github.io/r4ds-exercise-solutions/strings.html
00:15:35 Marielena Soilemezidi: awesome, thanks Daniel! :)
00:22:13 Marielena Soilemezidi: the book has the same solution for this, so you did great actually! :)
00:36:42 Shannon: https://regexcrossword.com/challenges/beginner
00:37:04 Marielena Soilemezidi: ooh cool!
00:37:10 Marielena Soilemezidi: thanks Shannon!
00:43:17 Marielena Soilemezidi: 0.0
00:43:26 Marielena Soilemezidi: who thought of this example?!
00:52:47 Daniel Adereti: Hey Marielena, if you want to volunteer for a class, you can fill in your name here: https://docs.google.com/spreadsheets/d/1zy2nXNkvcdqWuF8rQ5ApWRkVQG_UJt0azu3h_mEnY2E/edit#gid=0
00:55:10 Marielena Soilemezidi: Thanks, Daniel! I'll check the next available chapters :)
```
</details>
`r knitr::include_url("https://www.youtube.com/embed/9UuMNJB3KFA")`
<details>
<summary> Meeting chat log </summary>
```
00:03:15 Adeyemi Olusola: Good day!
00:04:23 Adeyemi Olusola: That sounds perfect
00:48:27 Adeyemi Olusola: I have to drop off now.
Thanks Shannon for the wonderful presentation!
00:49:28 Marielena Soilemezidi: oh, cool! :)
00:50:24 Marielena Soilemezidi: haha yaay!
00:51:47 Marielena Soilemezidi: weird!
```
</details>
### Cohort 7 {-}
`r knitr::include_url("https://www.youtube.com/embed/RFSURwQ0-eg")`
<details>
<summary> Meeting chat log </summary>
```
00:05:33 Oluwafemi Oyedele: Good Evening All, we will start in two minute time!!!
00:06:21 Olukunle Tunde: Okay
00:37:40 Oluwafemi Oyedele: https://stringr.tidyverse.org/articles/regular-expressions.html
00:47:09 Oluwafemi Oyedele: https://github.com/gadenbuie/regexplain
00:47:17 Oluwafemi Oyedele: https://www.garrickadenbuie.com/project/regexplain/
```
</details>
`r knitr::include_url("https://www.youtube.com/embed/5uuYHs3b_U8")`
<details>
<summary> Meeting chat log </summary>
```
00:29:46 Oluwafemi Oyedele: https://stringr.tidyverse.org/articles/regular-expressions.html
```
</details>
### Cohort 8 {-}
`r knitr::include_url("https://www.youtube.com/embed/wDVJ1zWGTzA")`