0.1 Introduction

To include a literal single or double quote in a string you can use\

0.2 Combining strings

str_c("x","y")
## [1] "xy"

Use the sep argument to control how they’re separated:

str_c("x","y", "z", sep = ",")
## [1] "x,y,z"

To combine multiple variables containing strings data

x<- c("a","b","c")
y<- c("d","e","f")
str_c(x,y)
## [1] "ad" "be" "cf"

Like most other functions in R, missing values are contagious. If you want them to print as “NA”, use str_replace_na()

x <- c("abc",NA)
str_c("|-",x,"-|")
## [1] "|-abc-|" NA
str_c("|-",str_replace_na(x),"-|")
## [1] "|-abc-|" "|-NA-|"

As shown above, str_c() is vectorised, and it automatically recycles shorter vectros to the same length as the longest:

str_c("prefix-",c("a","b","c"),"-suffix")
## [1] "prefix-a-suffix" "prefix-b-suffix" "prefix-c-suffix"

Objects of length 0 are silently dropped. This is particularly useful in conjunction with if:

name <- "Birasa"
time_of_day <- "morning"
birthday <- FALSE

str_c (
        "Good ", time_of_day," ", name,
        if(birthday) " and HAPPY BIRTHDAY",
        "."
)
## [1] "Good morning Birasa."

To collapse a vector of strings into a single string, use collapse:

str_c(c("x","y", "z"), collapse = ",")
## [1] "x,y,z"

1 Subsetting strings

You can extract parts of a string using str_sub(). As well as the string, str_sub() takes start and end arguments which give the (inclusive) positions of the substring:

x <- c("Apple","Banana","Pear")
str_sub(x,1,3)
## [1] "App" "Ban" "Pea"

Note that str_sub() won’t fail if the string is too short it will just return as much as possible:

str_sub("a",1,5)
## [1] "a"

You can also use the assignment form of str_sub() to modify strings:

str_sub(x,1,1) <- str_to_lower(str_sub(x,1,1))
x
## [1] "apple"  "banana" "pear"

2 exercises

  1. In code that doesn’t use stringr, you’ll often see paste() and paste0(). What’s the difference between the two functions? what stringr function are they equivalent to? How do the functions differ in their handling of NA?

  2. In your own words, describe the difference between the sep and collapse arguments to str_c().

  3. Use str_length() and str_sub() to extract the middle character from a string. what will you do if the string has an even number of characters?

  4. What does str_wrap() do? when might you want to use it?

  5. What does str_trim() do? what’s the opposite of str_trim()?

  6. Write a function that turns (e.g.) a vector c("a","b","c") into the string a,b, and c. Think carefully about what it should do if given a vector of length 0,10, or 2.

3 Matching patterns with regular expressions

Regexps are a very terse language that allow you to describe patters in strings. They take a little while to get your head around, but once understand them, you’ll find them extremely useful.

To learn regular expressions, we’ll use str_view() and str_view_all(). these functions take a character vector and a regular expression, and show you how they match. we’ll start with very simple regular expressions and then gradually get more and more complicated. Once you’ve mastered pattern matching, you’ll learn how to apply those ideas with various stringr functions.

3.1 Basic matches

the simplest patterns match exact strings:

x <- c("apple","banana","pear")
str_view(x,"an")

the next step up in complexity is ., which matches any character (except a newline):

str_view(x,".a.")

But if “.” matches any character, how do you match the character “.”? YOu need to use an “escape” to tell the regular expression you want to match it exactly, not use its special behaviour. Like strings, regexps use the backslash, \, to escape special behaviour. So to match an ., you need to 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 reate the regular expression \. we need the string \\.

# To create the regular expression we need \\
dot <- "\\."

# But the expression itself only contains one:
writeLines(dot)
## \.
# And this tells R to look for an explicit.
str_view(c("abc","a.c","bef"),"a\\.c")

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 . That means to match a literal  you need to write “\\\\” — you need four backslashes to match one!

x <- "a\\b"
writeLines(x)
## a\b
str_view(x,"\\\\")

3.2 Excercises

  1. Explain why each of these strings don’t match a \: ,\.\.

  2. How would you match the sequence "'\?

  3. What patterns will the regular expression \..\..\... match? How would you represent it as a string?

x <- "a\\..\\..\\..b"
writeLines(x)
## a\..\..\..b
str_view(x,"\\\\..\\\\..\\\\..")

4 Anchors

By default, regular expressions will match any part of a string. It’s often useful to anchor the regular expression so that it matches from the start or end of the string. You can use:

  • ^ to match the start of the string.
  • $ to match the end of the string.
x <- c("apple","banana","pear")
str_view(x,"^a")
str_view(x,"a$")

To force a regular expression to only match a complete string, anchor it with both ^ and $:

x <- c("apple pie","apple","apple cake")
str_view(x,"apple")
str_view(x,"^apple$")

5 Exercises

  1. How would you match the literal string "$^$"?
  2. Given the corpus of common words in stringr::words, create regular expressions that find all words that: 1. start with “y”. 2. End with “x”. 3. Are exactly three letters long. (Don’t cheat by using str_length()!) 4. Have seven letters or more. since this list is long, you might want to use the match argument to str_view() to show only the matching or non-matching words.

5.1 Solutions to above questions

# 1
v<- c("bira$^$sa","Fabrice","Nyirimana")
writeLines(v)
str_view(v,".[$^$$].")

#2.
## finding words which start by "y"
str_view(words,"^y", match = TRUE)

## finding words which end by "y"
str_view(words,"y$", match = TRUE)
## ## finding words which are exactly three letters
str_view(words,"^...$", match = TRUE)

## finding words which have seven letters and more
str_view(words,".......", match = TRUE)

6 Character classes and alternatives

there are a number of special patterns that match more than one character. You’ve already seen ., which matches any character apart from a newline. There are four other useful tools:

  • \d: matches any digit.
  • \s: matches any whitespace (e.g. space, tab, newline). *[^abc]: matches anything except a,b, or c.

Remember, to create a regular expression containing \d or \s, you’ll need to escape the \ for the string, so you’ll type \\d or \s

A character class containing a single character is a nice alternative to backslash escapes when you want to include a single metacharacter in a regex. Many people find this more readable.

# Look for a literal character that normally has special meaning in a regex
str_view(c("abc", "a.c", "a*c","in$^$ka", "a c"), ".[$.^$].")
str_view(c("abc", "a.c", "a*c", "a c"), ".[*]c")
str_view(c("abc", "a.c", "a*c", "a c"), "a ")
str_view(c("grey", "gray"), "gr(e|a)y")

7 Exercises

  1. Create regular expressions to find all words that: 1. start with a vowel. 2. that only contain consonants. (Hint: thinking about matching “not”-vowels.) 3. End with ed, but not with eed. 4. End with ing or ise.

  2. Empirically verify the rule “i before e except after c”.

  3. Is “q” always followed by a “u”?

  4. Write a regular expression that matches a word if it’s probably written in British English, not American English.

  5. Create a regular expression that will match telephone numbers as commonly written in your country.

7.1 Solutions

# 1.
 ## 1 Vowel starting
str_view(c("Birasa","angel","iabraham","incorrect","gmail"), "^[aiuoe]", match = TRUE)
## 2 only contain consonants.
str_view(c("Birasa","angel","FLY","incorrect","gmail"), "[aiuoe]", match = FALSE)
## 3. strings that start with or end with ed, but not with eed
str_view(c("Saeed","Seed","Fed","Fred","education"), "^ed|[^e]ed$")
## 4 end with ing or ise
str_view(c("Seeing","Seeding","Fedise","Freding","education"), "(ing|ise)$", match = TRUE)
# 2 Empirically verify the rule "i before e except after c"
str_view(c("Saeid","Siejk","Fed","Fred","educieation"),"[^c]ie|cei")
# Empirically verify the rule "i before e except after c"
str_view(words, "ie|cei", match = TRUE)
# Is "q" always followed by a "u"
str_view(words,"(qu|uq)",match = TRUE)
g<- c("queque","euqrope","eunice","oecumenisme","euclesiastica")
str_view(g,"(qu|uq)+",match = TRUE)
#4. Write a regular expression that matches a word if it's probably written in British English, not American English

str_view(words,"ou|ise$|ae|oe|yse$", match = TRUE)
# 5. Create a regular expression that will match telephone numbers as commonly written in your country.
phones<-c("(250) 787564662","(+250) 725114672", "(+250) 786375547","123-456-7890", "(123)456-7890", "(123) 456-7890", "1235-2351")

# the following function read in the phone numbers with RWandan Format and similar (first format)
str_view(phones,"\\(\\d\\d\\d\\)\\s*\\d\\d\\d\\d\\d\\d\\d\\d\\d")
# the following function read in the phone numbers with RWandan Format and similar (second and third format)
str_view(phones,"\\(\\+\\d\\d\\d\\)\\s*\\d\\d\\d\\d\\d\\d\\d\\d\\d")
# or you can replace "\\s*" with [ ] to find the literal space, like in the following code
str_view(phones,"\\(\\+\\d\\d\\d\\)[ ]\\d\\d\\d\\d\\d\\d\\d\\d\\d")
# To match the format of the US phone numbers without parantheses, one could go like the following:

str_view(phones,"\\(\\d\\d\\d\\)\\s*\\d\\d\\d-\\d\\d\\d\\d")
# or with parantheses like the following, as mentioned in the above phone number of phones variable:

str_view(phones,"\\d\\d\\d\\d-\\d\\d\\d\\d")

7.2 Repetition

The next step up in power involves controlling how many times a pattern matches: * ?: 0 or 1 * +: 1 or more * *: 0 or more

x <- "1888 is the longest year in Roman numerals: MDCCCLXXXVIII"
str_view(x,"CC?")
str_view(x,"CC+")
str_view(x,'C+[LX]+')

You can also specify the number of matches precisely: * {n}: exactly n * {n,}: n or more * {,m}: at most m * {n,m}: between n and m

str_view(x,'C{2}')
str_view(x,'C{2,}')

### between 2 and 3

str_view(x,'C{2,3}')

By default these matches are “greedy”: they will match the longest string possible. You can make them “lazy”, matching the shortest string possible by putting a ? after them. This is an advanced feature of regular expressions, but it’s useful to know that it exists:

7.2.1 between 2 and 3 but with minimum consideration

str_view(x,'C{2,3}?')

7.2.2 finding the text containing C and either L or X but with focus on the minimum

str_view(x,'C[LX]?')

7.2.3 finding texts ending with letter “d”

h<-c("12ga","birasa","ingabire12","digidig")
str_view(h,'d$', match = TRUE)

7.2.3.1 finding texts ending with any number

h<-c("12ga","birasa","ingabire12","digidig")
str_view(h,'\\d+$', match = TRUE)

7.2.3.2 finding texts starting with any numbers

h<-c("12ga","birasa","ingabire12","digidig")
str_view(h,'^\\d+', match = TRUE)
  1. Create regular expressions to find all words that:

  2. Start with three consonants.

  3. Have three or more vowels in a row.

  4. Have two or more vowel-consonant pairs in a row.

str_view(words,"^[^aieuo]{3}", match = TRUE)

7.2.4 Have three or more vowels in a row.

str_view(words,"(?:[aeiou][^aeiou]*){3,}", match = TRUE)

7.2.5 Finding all words containing small letters only

str_view(words,"[a-z]", match = TRUE)

7.2.5.1 So, ^.*$ means - match, from beginning to end, any character that appears zero or more times. Basically, that means - match everything from start to end of the string. This regex pattern is not very useful.

str_view(words,"^.*$", match = TRUE)

7.2.5.2 Let’s take a regex pattern that may be a bit useful. Let’s say I have two strings The bat of Matt Jones and Matthew's last name is Jones. The pattern ^Matt.*Jones$ will match Matthew's last name is Jones. Why? The pattern says - the string should start with Matt and end with Jones and there can be zero or more characters (any characters) in between them.

n <- c("matthew's last name is Jones","The bat of Matt Jones")

str_view(n,"^Matthew.*Jones$", match = TRUE)