---
title: Regular expressions
author: David Kane
tutorial:
  id: regular-expressions
output:
  learnr::tutorial:
    progressive: yes
    allow_skip: yes
runtime: shiny_prerendered
description: 'Tutorial for Chapter 15: Regular expressions'
---

```{r setup, include = FALSE}
library(learnr)
library(tutorial.helpers)
library(tidyverse)
knitr::opts_chunk$set(echo = FALSE)
options(tutorial.exercise.timelimit = 60, 
        tutorial.storage = "local") 

people <- tribble(
  ~str,
  "<Sheryl>-F_34",
  "<Kisha>-F_45", 
  "<Brandon>-N_33",
  "<Sharon>-F_38", 
  "<Penny>-F_58",
  "<Justin>-M_41", 
  "<Patricia>-F_84", 
)
```

```{r copy-code-chunk, child = system.file("child_documents/copy_button.Rmd", package = "tutorial.helpers")}
```

```{r info-section, child = system.file("child_documents/info_section.Rmd", package = "tutorial.helpers")}
```

<!-- DK: Knowledge drop the two links from the summary. -->

<!-- Maybe one of the nice looking babynames lots could go at the end of this tutorial. Or maybe we make a new one? -->

<!-- This tutorial is long! Maybe don't include the practice section? Or maybe it is just long to write! -->


## Introduction
### 

This tutorial covers [Chapter 15: Regular expressions](https://r4ds.hadley.nz/regexps.html) from [*R for Data Science (2e)*](https://r4ds.hadley.nz/) by Hadley Wickham, Mine Çetinkaya-Rundel, and Garrett Grolemund. With the help of the [**stringr**](https://stringr.tidyverse.org/index.html) package, we use regular expressions, a concise and powerful language for describing patterns within strings. 


## Pattern basics
### 

The term "regular expression" is a bit of a mouthful, so most people abbreviate it to "regex" or "regexp."


### Exercise 1

Run `library(tidyverse)` in the Console. Copy/paste the resulting message.

```{r pattern-basics-1}
question_text(NULL,
	answer(NULL, correct = TRUE),
	allow_retry = TRUE,
	try_again_button = "Edit Answer",
	incorrect = NULL,
	rows = 3)
```

### 

There are currently nine core packages in the *Tidyverse*, although that number may grow over time. **stringr** is the main package with which we use regular expressions.


### Exercise 2

Load the **babynames** package with `library()` below. Don't forget to hit "Run Code."


```{r pattern-basics-2, exercise = TRUE}

```

```{r pattern-basics-2-hint-1, eval = FALSE}
library(...)
```

There is no return value.


### Exercise 3

In the Console, run `library(babynames)` and then look up the help page for the `babynames` tibble by running `?babynames`. Copy/paste the Format information below.

```{r pattern-basics-3}
question_text(NULL,
	answer(NULL, correct = TRUE),
	allow_retry = TRUE,
	try_again_button = "Edit Answer",
	incorrect = NULL,
	rows = 3)
```

### 

The definition of the `prop` variable is subtle.




### Exercise 4

Run `glimpse()` on the `babynames` tibble.

```{r pattern-basics-4, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r pattern-basics-4-hint-1, eval = FALSE}
glimpse(...)
```

### 

There are almost 2 millions rows! 

### Exercise 5

Run `fruit` at the Console. CP/CR.

```{r pattern-basics-5}
question_text(NULL,
	answer(NULL, correct = TRUE),
	allow_retry = TRUE,
	try_again_button = "Edit Answer",
	incorrect = NULL,
	rows = 15)
```

### 

In addition to `babynames`, we will use three character vectors from the **stringr** package:

* `fruit` contains the names of 80 fruits.
* `words` contains 980 common English words.
* `sentences` contains 720 short sentences.


### Exercise 6

Type `str_view(fruit, "berry")`. Hit "Run Code." (Unless we specifically indicate that you should use the Console, you should enter commands into the exercise code block and press "Run Code.") 

The first argument to `str_view()` is the vector which you are searching through. The second argument is the regular expression which you are searching for.


```{r pattern-basics-6, exercise = TRUE}

```

```{r pattern-basics-6-hint-1, eval = FALSE}
str_view(fruit, "berry")
```

### 

`str_view()` will show only the elements of the string vector that match, surrounding each match with <>, and, where possible, highlighting the match in blue.


### Exercise 7

Letters and numbers match exactly and are called *literal characters*. Most punctuation characters, like `.`, `+`, `*`, `[`, `]`, and `?`, have special meanings and are called *metacharacters*.

Run `str_view()` with `c("a", "ab", "ae", "bd", "ea", "eab")` as the first argument --- this is the vector which we are searching through --- and `"a."` as the second argument.

A `.` will match any character, so `"a."` will match any string that contains an “a” followed by another character. 


```{r pattern-basics-7, exercise = TRUE}

```

```{r pattern-basics-7-hint-1, eval = FALSE}
str_view(c("a", "ab", "ae", "bd", "ea", "eab"), ...)
```

### 

There are three matches. Notice how the `<>` pull out the actual match itself, leaving irrelevant letters, like the "e" in the last match, outside.

### Exercise 8

Run `str_view()` on `fruit` as the vector and `"a...e"` as the pattern.

```{r pattern-basics-8, exercise = TRUE}

```

```{r pattern-basics-8-hint-1, eval = FALSE}
str_view(..., "a...e")
```

Try to interpret the pattern before we tell you the answer . . .

### 

The matches are any fruit which includes an "a", followed by any three characters, followed by an "e". Look at the matches to confirm that they all follow this rule.


### Exercise 9

*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).

Run `str_view()` on `c("a", "ab", "abb")` as the vector and `"ab?"` as the pattern.


```{r pattern-basics-9, exercise = TRUE}

```

```{r pattern-basics-9-hint-1, eval = FALSE}
str_view(c("a", "ab", "abb"), ...)
```

### 

Note how "a" matches "ab?" because the "?" makes the "b" optional.

### Exercise 10

Run `str_view()` on `c("a", "ab", "abb")` as the vector and `"ab+"` as the pattern.

```{r pattern-basics-10, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r pattern-basics-10-hint-1, eval = FALSE}
str_view(..., "ab+")
```

### 

`"ab+"` matches an "a", followed by at least one "b".

### Exercise 11

Run `str_view()` on `c("a", "ab", "abb")` as the vector and `"ab*"` as the pattern.

```{r pattern-basics-11, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r pattern-basics-11-hint-1, eval = FALSE}
str_view(c("a", "ab", "abb"), ...)
```

### 

`ab*` matches an "a", followed by any number of "b"s, including zero "b"s.

### Exercise 12

*Character classes* are defined by `[]` and let you match a set of characters, e.g., `[abcd]` matches “a”, “b”, “c”, or “d”.

Run `str_view()` on `words` as the vector and `"[aeiou]x[aeiou]"` as the pattern.

```{r pattern-basics-12, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r pattern-basics-12-hint-1, eval = FALSE}
str_view(..., "[aeiou]x[aeiou]")
```

Can you explain what is going on?

### 

We are matching all the words which feature the patter of any vowel, followed by an "x", followed by any vowel.

### Exercise 13

You can also invert the match by starting with ^: [^abcd] matches anything except “a”, “b”, “c”, or “d”. 

Run `str_view()` on `words` as the vector and `"[^aeiou]y[^aeiou]"` as the pattern.

```{r pattern-basics-13, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r pattern-basics-13-hint-1, eval = FALSE}
str_view(words, "...")
```

### 

The `"[^aeiou]y[^aeiou]"` pattern finds every instance of any non-vowel, followed by "y", followed by any non-vowel.

### Exercise 14

You can use alternation, `|`, to pick between one or more alternative patterns.

Run `str_view()` on `fruit` as the vector and `"apple|melon|nut"` as the pattern.

```{r pattern-basics-14, exercise = TRUE}

```

```{r pattern-basics-14-hint-1, eval = FALSE}
str_view(..., "apple|melon|nut")
```

### 

The `"apple|melon|nut"` pattern matches any fruit which contains one of the three options.

### Exercise 15

Run `str_view()` on `fruit` as the vector and `"aa|ee|ii|oo|uu"` as the pattern. his should find all the fruits with at least one repeated vowel.

```{r pattern-basics-15, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r pattern-basics-15-hint-1, eval = FALSE}
str_view(fruit, ...)
```

### 

Regular expressions are very compact and use a lot of punctuation characters, so they can seem overwhelming and hard to read at first. Don’t worry; you’ll get better with practice, and simple patterns will soon become second nature. 


## Key functions
### 

Now that you’ve got the basics of regular expressions under your belt, let’s use them with some **stringr** and **tidyr** functions. In the following section, you’ll learn how to detect the presence or absence of a match, how to count the number of matches, how to replace a match with fixed text, and how to extract text using a pattern.

### Exercise 1

`str_detect()` returns a logical vector that is `TRUE` if the pattern matches an element of the character vector and `FALSE` otherwise.  Run `str_detect()` with the `string` argument equal to `c("a", "b", "c")` and the `pattern` argument equal to  `"[aeiou]"`.

```{r key-functions-1, exercise = TRUE}

```

```{r key-functions-1-hint-1, eval = FALSE}
str_detect(string = ..., 
           pattern = ...)
```

### 

Most of the time, we admit the argument names (`string` and `pattern`) and simply rely on "positional mapping," meaning that `string` is always the first argument (and `pattern` the second) because that is how the function is defined.

### Exercise 2

Since `str_detect()` returns a logical vector of the same length as the initial vector, it pairs well with `filter()`. Pipe `babynames` to `filter()`, using `str_detect(name, "x")` as the argument to `filter()`.

```{r key-functions-2, exercise = TRUE}

```

```{r key-functions-2-hint-1, eval = FALSE}
babynames |> 
  filter(...(name, ...))
```

### 

Note that `name` is one of the variables in `babynames`. This pipe reduces the almost 2 million rows in `babynames` to just the 16,000 or so rows in which `name` contains the letter `x`.


### Exercise 3

<!-- DK: Note how the `n` column in `babynames` makes this code harder than we would like. Maybe break this up into three steps? -->

Continue the pipe with `count()`, using the arguments `name, wt = n, sort = TRUE`. 


```{r key-functions-3, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r key-functions-3-hint-1, eval = FALSE}
... |> 
  count(..., wt = n, ... = TRUE)
```

### 

We want to determine the most popular `name`'s with an "x" in them, which is why we have `sort = TRUE`. We need `wt = n` because we want to account for the role of `n` in indicating how many times, in a single year, a given names was used.

### Exercise 4

<!-- DK: Should this be `x` or `string` in this paragraph? -->

We can also use `str_detect()` with `summarize()` by pairing it with `sum()` or `mean()`: `sum(str_detect(x, pattern))` tells you the number of observations that match and `mean(str_detect(x, pattern))` tells you the proportion that match. 

Pipe `babynames` to `summarize()` with the argument `prop_x = mean(str_detect(name, "x"))`.

```{r key-functions-4, exercise = TRUE}

```

```{r key-functions-4-hint-1, eval = FALSE}
babynames |> 
  summarize(prop_x = ...(str_detect(name, ...)))
```

### 

The result indicates that about 0.8% of the `names` in `babynames` include the letter "x."

### Exercise 5

We are interested in how this percentage has changed over time, so modify the code by adding `.by = year` to the class to `summarize()`.

```{r key-functions-5, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r key-functions-5-hint-1, eval = FALSE}
babynames |> 
  summarize(... = mean(str_detect(name, "x")),
            .by = ...)
```

### 

This gives us the proportion of names that contain an "x." if you wanted the proportion of babies with a name containing an "x," you would need to perform a weighted mean.

### Exercise 6

Continue the pipe to a call to `ggplot()`, with `aes(x = year, y = prop_x)`. Add `geom_line()`. Don;t forget that commands after `ggplot()` are separated by `+`, not `|>`.

```{r key-functions-6, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r key-functions-6-hint-1, eval = FALSE}
... |> 
  ggplot(aes(x = ..., ... = prop_x)) + 
  geom_...()
```

### 

There are two functions that are closely related to `str_detect()`:  

* `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.


### Exercise 7

The next step up in complexity from `str_detect()` is `str_count()`: rather than a true or false, it tells you how many matches there are in each string. Run `str_count()` with two arguments: the vector `c("apple", "banana", "pear")` and the letter "p".

```{r key-functions-7, exercise = TRUE}

```

```{r key-functions-7-hint-1, eval = FALSE}
str_count(..., "p")
```

### 

There are two "p"'s in "apple" but one in "pear."

### Exercise 8

Note that each match starts at the end of the previous match, i.e. regex matches never overlap. 

Run `str_count()` on `"abababa"` and `"aba"`.


```{r key-functions-8, exercise = TRUE}

```

```{r key-functions-8-hint-1, eval = FALSE}
str_count("abababa", ...)
```

### 

For example, in "abababa", how many times will the pattern "aba" match? Regular expressions say two, not three.

### Exercise 9

To better see this, `str_view()` on `"abababa"` and `"aba"`.

```{r key-functions-9, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r key-functions-9-hint-1, eval = FALSE}
str_view(..., "aba")
```

### 

In other words, the "second" "aba" string, which relies on the second "a" in the first "aba" string, does not count because regular expressions, by default, are exclusive.


### Exercise 10

Pipe `babynames` to `count(name)`.


```{r key-functions-10, exercise = TRUE}

```

```{r key-functions-10-hint-1, eval = FALSE}
babynames |> 
  ...(name)
```

### 

The reduces the almost 2 million entries to just the 100,000 or so unique names. Note how often the letter "a" appears in the first ten names.

### Exercise 11

Continue the pipe with `mutate()`, creating a new variable, `vowels`, which is equal to `str_count(name, "[aeiou]")`.

```{r key-functions-11, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r key-functions-11-hint-1, eval = FALSE}
babynames |> 
  count(name) |> 
  mutate(
    vowels = ...(..., "[aeiou]")
  )
```

### 

If you look closely, you’ll notice that there’s something off with our calculations: “Aaban” contains three “a”s, but our summary reports only two vowels. That’s because regular expressions are case sensitive. Ignore that error for now.


### Exercise 12

Add another variable creation argument to `mutate()`. Create `consonants` as the result of `str_count(name, "[^aeiou]")`.

```{r key-functions-12, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r key-functions-12-hint-1, eval = FALSE}
babynames |> 
  count(name) |> 
  mutate(
    vowels = str_count(name, "[aeiou]"),
    consonants = ...(name, ...)
  )
```

### 

This suffers from the same problem as `vowels`. The capital letter "A" is not in the set `aeiou`, so it is counted, incorrectly, as a consonant. Among, other approaches, we could fix this by:

* Adding the upper case vowels to the character class: `str_count(name, "[aeiouAEIOU]")`.

* Telling the regular expression to ignore case: `str_count(name, regex("[aeiou]", ignore_case = TRUE))`.

### Exercise 13

Change `mutate()` so that the first step is to change-in-place the variable `name` to be all in lower case. We do that with `name = str_to_lower(name)`.

```{r key-functions-13, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r key-functions-13-hint-1, eval = FALSE}
babynames |> 
  count(name) |> 
  mutate(
    ... = ...(name),
    vowels = str_count(name, "[aeiou]"),
    consonants = str_count(name, "[^aeiou]")
  )
```

### 

This variety of approaches is pretty typical when working with strings — there are often multiple ways to reach your goal, either by making your pattern more complicated or by doing some preprocessing on your string. If you get stuck trying one approach, it can often be useful to switch gears and tackle the problem from a different perspective.

### Exercise 14

As well as detecting and counting matches, we can also modify them with `str_replace()` and `str_replace_all()`. Run `str_replace_all()` on with `string` equal to `c("apple", "pear", "banana")`, `pattern` equal to  `"[aeiou]"`, and `replacement` equal to `"-"`.


```{r key-functions-14, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r key-functions-14-hint-1, eval = FALSE}
str_replace_all(string = ..., 
                ... = "[aeiou]", 
                replacement = ...)
```

### 

We usually omit the argument names, so this code would normally be: `str_replace_all(c("apple", "pear", "banana"), "[aeiou]", "-")`.

### Exercise 15

`str_remove()` and `str_remove_all()` are handy shortcuts for `str_replace(x, pattern, "")`. Run `str_remove_all()` on `c("apple", "pear", "banana")` and `"[aeiou]"`.


```{r key-functions-15, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r key-functions-15-hint-1, eval = FALSE}
str_remove_all(c("apple", "pear", "banana"), ...)
```

### 

These commands with the `_all` suffix just act on the *first* match in each element of the vector. Try `str_remove(c("apple", "pear", "banana"), "[aeiou]")` for an example.

These functions are naturally paired with `mutate()` when doing data cleaning, and you’ll often apply them repeatedly to peel off layers of inconsistent formatting.

### Exercise 16

The last function we’ll discuss in this section uses regular expressions to extract data out of one column into one or more new columns: `separate_wider_regex()`. It’s a peer of the `separate_wider_position()` and `separate_wider_delim()` functions that you learned about previously. These functions live in the **tidyr** package because they operate on (columns of) data frames, rather than individual vectors.

Run `people` to examine the tibble which we will use.

```{r key-functions-16, exercise = TRUE}

```

```{r key-functions-16-hint-1, eval = FALSE}
people
```

### 

We have the name, gender, and age of a bunch of people in a rather weird format. We wish we could reassure you that you’d never see something this weird in real life, but unfortunately over the course of your career you’re likely to see much weirder!

### Exercise 17

To extract this data using separate_wider_regex() we just need to construct a sequence of regular expressions that match each piece. If we want the contents of that piece to appear in the output, we give it a name. Consider:

<!-- DK: Shouldn't we split this up into a series of steps, perhaps with on-purpose errors and debugging? -->

```{r key-functions-17, exercise = TRUE}
people |> 
  separate_wider_regex(
    str,
    patterns = c(
      "<", 
      name = "[A-Za-z]+", 
      ">-", 
      gender = ".", "_", 
      age = "[0-9]+"
    )
  )
```

```{r key-functions-17-hint-1, eval = FALSE}
No hint needed!
```

### 

<!-- DK: Note the typo in the original in R4DS. -->

If the match fails, you can use `too_few = "debug"` to figure out what went wrong, just like `separate_wider_delim()` and `separate_wider_position()`.


## Pattern details
### 

<!-- DK: Revisit this section. There are too many paragraphs of prose. And too many instructions to just run some code. -->

Now that you understand the basics of the pattern language and how to use it with some stringr and tidyr functions, it’s time to dig into more of the details. First, we’ll start with **escaping**, which allows you to match metacharacters that would otherwise be treated specially. Next, you’ll learn about **anchors** which allow you to match the start or end of the string. Then, you’ll learn more about **character classes** and their shortcuts which allow you to match any character from a set. Next, you’ll learn the final details of **quantifiers** which control how many times a pattern can match. Then, we have to cover the important (but complex) topic of **operator precedence** and parentheses. And we’ll finish off with some details of **grouping** components of the pattern.


### Exercise 1

In order to match a literal `.`, you need an escape which tells the regular expression to match metacharacters literally. Like strings, regexps use the backslash for escaping. So, to match a `.`, you need the regexp `\.`. Unfortunately this creates a problem. We use strings to represent regular expressions, and `\` is also used as an escape symbol in strings. So to create the regular expression `\.` we need the string `"\\."`, as the following example shows. Run this code:


```{r pattern-details-1, exercise = TRUE}
# 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")
```

```{r pattern-details-1-hint-1, eval = FALSE}
No hint needed.
```

### 

In this section, we’ll usually write regular expression without quotes, like `\.`. If we need to emphasize what you’ll actually type, we’ll surround it with quotes and add extra escapes, like `"\\."`.


### Exercise 2

If `\` is used as an escape character in regular expressions, how do you match a literal `\`? Well, you need to escape it, creating the regular expression `\\`. To create that regular expression, you need to use a string, which also needs to escape `\`. 


```{r pattern-details-2, exercise = TRUE}
x <- "a\\b"
str_view(x)
str_view(x, "\\\\")
```

```{r pattern-details-2-hint-1, eval = FALSE}
No hint needed.
```

### 

In other words, to match a literal `\` you need to write `"\\\\"` — you need four backslashes to match one!

### Exercise 3

Probably better is to use the raw strings you learned about previously. That lets you avoid one layer of escaping. Run this code.

```{r pattern-details-3, exercise = TRUE}
x <- "a\\b"
str_view(x, r"{\\}")
```

```{r pattern-details-3-hint-1, eval = FALSE}
No hint needed.
```

### 

`str_view()` highlights the single backslash which is part of the `x` variable. But, to match that single backslash, we need a raw string pattern with two blackslashes.

### Exercise 4

If you’re trying to match a literal `.`, `$`, `|`, `*`, `+`, `?`, `{`, `}`, `(`, `)`, there’s an alternative to using a backslash escape: you can use a character class: `[.]`, `[$]`, `[|]`, ... all match the literal values. Run this code.

```{r pattern-details-4, exercise = TRUE}
str_view(c("abc", "a.c", "a*c", "a c"), "a[.]c")
str_view(c("abc", "a.c", "a*c", "a c"), ".[*]c")
```

```{r pattern-details-4-hint-1, eval = FALSE}
No hint needed.
```

### 

Regular expressions require care and attention. That last example, which uses `".[*]c"`, means any character (the `.`), followed by a `*` (captured in as a character class), followed by a "c". Only `"a*c"` matches this pattern.

### Exercise 5

By default, regular expressions will match any part of a string. If you want to match at the start or end you need to *anchor* the regular expression using `^` to match the start or `$` to match the end. Run `str_view()` with `fruit` as the first argument and `"^a"` as the second.

```{r pattern-details-5, exercise = TRUE}

```

```{r pattern-details-5-hint-1, eval = FALSE}
str_view(fruit, ...)
```

### 

Although there are many fruits that include the letter "a," only three begin with the letter "a."

### Exercise 6

Run `str_view()` with `fruit` as the first argument and `"a$"` as the second.

```{r pattern-details-6, exercise = TRUE}

```

<button onclick = "transfer_code(this)">Copy previous code</button>

```{r pattern-details-6-hint-1, eval = FALSE}
str_view(fruit, ...)
```

### 

Note how we use `"^a"` with the `^` at the front of the pattern, indicating that "a" belongs at the start of the string, and `"a$`, with the `$` at the end of the pattern, indicating that "a" belongs at the end of the string.

### Exercise 7

To force a regular expression to match only the full string, anchor it with both `^` and `$`. Run `str_view()` twice, both times with `fruit` as the first argument. In one, `"apple"` is the pattern. In another, `"^apple$` is the pattern.

```{r pattern-details-7, exercise = TRUE}

```

```{r pattern-details-7-hint-1, eval = FALSE}
str_view(..., "apple")
str_view(fruit, ...)
```

### 

Are you getting overwhelmed yet? No worries. The rise of ChatGPT and other AI tools makes the creation of regular expressions to do exactly what you want much easier.

### Exercise 8

You can also match the boundary between words (i.e. the start or end of a word) with `\b`. This can be particularly useful when using RStudio’s find and replace tool. Add `str_view(x, "sum")` to this exercise block

```{r pattern-details-8, exercise = TRUE}
x <- c("summary(x)", "summarize(df)", "rowsum(x)", "sum(x)")

```

```{r pattern-details-8-hint-1, eval = FALSE}
x <- c("summary(x)", "summarize(df)", "rowsum(x)", "sum(x)")
str_view(..., "sum")
```

### 

Every element of our vector matches the pattern.

### Exercise 9

Add `str_view(x, "\\bsum\\b")` to this exercise block

```{r pattern-details-9, exercise = TRUE}
x <- c("summary(x)", "summarize(df)", "rowsum(x)", "sum(x)")

```

```{r pattern-details-9-hint-1, eval = FALSE}
x <- c("summary(x)", "summarize(df)", "rowsum(x)", "sum(x)")
str_view(x, ...)
```

### 

This is not easy. `\b` means a word boundary, which includes the implicit start of the words "summary()," "summarize()," and "sum()." The first two, however, do not match the `\b` end of the pattern. Yet "sum()" does match because `(`, not being a letter, counts as a boundary. Finally, we need to escape the `\` for both usages of `\b`, leading to `\\b` at both the front and the back.


### Exercise 10

<!-- DK: need to start with a simple example which explains using a vector of parameters. -->

When used alone, anchors will produce a zero-width match. Run `str_view()` on the string "abc" and the patterns `c("$", "^", "\\b")`.

```{r pattern-details-10, exercise = TRUE}

```

```{r pattern-details-10-hint-1, eval = FALSE}
str_view(..., c("$", "^", "\\b"))
```

### 

Note how we have a single string but three different patterns, each of which does match the string. The display, with its zero-width brackets --- `<>` --- indicates that the matches do not involve the contents of the string.

### Exercise 11

The previous example helps you understand what happens when you replace a standalone anchor. Run `str_replace_all()` on the string `"abc"` and the patterns `c("$", "^", "\\b")` with using  `"--"` as the replacement. 


```{r pattern-details-11, exercise = TRUE}

```

```{r pattern-details-11-hint-1, eval = FALSE}
str_replace_all("abc", ..., "--")
```

### 

Even though the input is a single string, the three patterns generate a vector of three elements as the output. 


<!-- ## Pattern control -->
<!-- ###  -->

<!-- ## Practice -->
<!-- ###  -->

<!-- ## Regular expressions in other places -->
<!-- ###  -->

## Summary
### 

This tutorial covered [Chapter 15: Regular expressions](https://r4ds.hadley.nz/regexps.html)  from [*R for Data Science (2e)*](https://r4ds.hadley.nz/) by Hadley Wickham, Mine Çetinkaya-Rundel, and Garrett Grolemund. With the help of the [**stringr**](https://stringr.tidyverse.org/index.html) package, we used regular expressions, a concise and powerful language for describing patterns within strings. 

If you want to learn more, a good place to start is [`vignette("regular-expressions", package = "stringr")`](https://stringr.tidyverse.org/articles/regular-expressions.html): it documents the full set of syntax supported by the [**stringr**](https://stringr.tidyverse.org/index.html) package. Another useful reference is `https://www.regular-expressions.info/`. It’s not R specific, but you can use it to learn about the most advanced features of regexes and how they work under the hood.

```{r download-answers, child = system.file("child_documents/download_answers.Rmd", package = "tutorial.helpers")}
```
