๐ก R String Manipulation & Regex
๐ง What this note covers
Text data shows up constantly in real world work, whether it is cleaning up messy survey responses, extracting information from log files, or parsing dates out of filenames. This note covers Rโs base functions for working with character strings, then introduces regular expressions, a mini language for describing text patterns that dramatically expands what you can search for and extract.
โ๏ธ Basic string functions
nchar("hello") # 5, counts the number of characters
toupper("hello") # "HELLO"
tolower("HELLO") # "hello"
trimws(" hello ") # "hello", removes leading and trailing whitespace
substr("hello world", 1, 5) # "hello", extracts characters from position 1 through 5substr can also assign, not just extract
A less known feature is that
substr()can be used on the left side of an assignment to replace a portion of a string in place, for examplesubstr(x, 1, 1) <- "H"would replace just the first character ofx.
๐ Combining strings
paste("hello", "world") # "hello world", joins with a space by default
paste("hello", "world", sep = "-") # "hello-world", custom separator
paste0("hello", "world") # "helloworld", shorthand for sep = ""
paste(c("a", "b", "c"), collapse = ", ") # "a, b, c", joins ALL elements of a vector into one stringpaste versus paste0, and the collapse trap
paste0()is simplypaste()with the separator already set to an empty string, so use it whenever you want to glue text together with no space in between. Separately, thecollapseargument is easy to confuse withsep. Usesepto control the space between two separate arguments you passed in, and usecollapseto control how the multiple elements inside a single vector get flattened into one combined string.
๐ Splitting strings
strsplit("a,b,c", split = ",")
# returns a LIST containing one character vector: "a" "b" "c"
strsplit("a,b,c", split = ",")[[1]] # unwrap the list to get the plain vector directlyWhy strsplit returns a list
strsplit()is designed to work on an entire vector of strings at once, potentially splitting each one into a different number of pieces. Since the results can have different lengths for each input string, a list is the only structure flexible enough to hold them all consistently, which is the same underlying reasoning covered forlapply()in R Apply Family.
๐ Searching within strings
grepl("wor", "hello world") # TRUE, checks whether the pattern exists, returns logical
grep("wor", c("hello world", "goodbye")) # 1, returns the POSITION of matches within a vector
grep("wor", c("hello world", "goodbye"), value = TRUE) # "hello world", returns the actual matching values
sub("world", "there", "hello world") # "hello there", replaces only the FIRST match
gsub("o", "0", "hello world") # "hell0 w0rld", replaces ALL matches ("global sub")Remembering sub versus gsub
The โgโ in
gsub()stands for โglobal,โ meaning it replaces every single match it finds in the string, while plainsub()stops after replacing just the first match it encounters.
๐งฌ Regular expressions
A regular expression, often shortened to โregex,โ is a compact pattern language for describing text to search for, far more powerful than searching for an exact literal phrase. All of the searching functions above, grepl(), grep(), sub(), and gsub(), actually accept full regular expressions in their pattern argument, not just plain literal text.
Common regex building blocks
| Pattern | Meaning | Example match |
|---|---|---|
. | Any single character | a.c matches โabcโ, โaxcโ |
* | Zero or more of the previous character | ab* matches โaโ, โabโ, โabbbโ |
+ | One or more of the previous character | ab+ matches โabโ, โabbbโ, but not โaโ |
? | Zero or one of the previous character | colou?r matches โcolorโ and โcolourโ |
^ | Anchors to the start of the string | ^hello only matches at the beginning |
$ | Anchors to the end of the string | world$ only matches at the very end |
[abc] | Any one character from this set | [aeiou] matches any single vowel |
[^abc] | Any character NOT in this set | [^0-9] matches anything that is not a digit |
\\d | Any digit | matches โ0โ through โ9โ |
\\w | Any word character (letter, digit, underscore) | |
\\s | Any whitespace character | |
{n} | Exactly n repetitions of the previous element | \\d{4} matches exactly four digits |
grepl("^\\d{3}-\\d{4}$", "555-1234") # TRUE, checks for a phone number style pattern
gsub("[aeiou]", "*", "hello world") # "h*ll* w*rld", replaces every vowelDouble backslashes in R strings
In R, a single backslash inside a regular string is used as an escape character for things like
\n(newline) and\t(tab). Because of this, if you want to actually type a literal backslash for regex purposes, such as\dfor โany digit,โ you need to double it up as\\dso that R correctly interprets it as a single literal backslash being passed into the regex engine.
Extracting matched text
text <- "Order number: 48291"
regmatches(text, regexpr("\\d+", text))
# "48291", extracts the actual matched digits out of the stringregexpr finds the position, regmatches extracts the text
These two functions are almost always used together.
regexpr()finds where in the string a match starts and how long it is, whileregmatches()uses that positional information to actually pull out the matching substring itself. If you need every match in a string rather than just the first one, usegregexpr()paired withregmatches()instead.
๐ฆ stringr: the tidyverse alternative
Base Rโs string functions work fine, but their names are famously inconsistent, for instance grepl() and nchar() sound nothing alike despite being related. The stringr package, part of the tidyverse, offers a much more consistently named set of functions that all start with str_.
library(stringr)
str_detect("hello world", "wor") # equivalent to grepl()
str_replace("hello world", "world", "there") # equivalent to sub()
str_replace_all("hello world", "o", "0") # equivalent to gsub()
str_split("a,b,c", ",") # equivalent to strsplit()
str_extract("Order number: 48291", "\\d+") # equivalent to regmatches + regexpr combinedWhy many people prefer stringr
Beyond the consistent naming, every stringr function is also designed so the string being worked on is always the first argument, which makes it fit naturally into tidyverse pipe chains, a style covered in R dplyr & Tidyverse.
๐ Where to go next
Text manipulation frequently overlaps with cleaning messy real world data, so R Data Import Export and R dplyr & Tidyverse are natural next stops. If your text data includes dates buried inside strings, R Dates & Times covers how to parse them out properly.