Introduction to stringr

There are four main families of functions in stringr:

  1. Character manipulation: these functions allow you to manipulate individual characters within the strings in character vectors.

  2. Whitespace tools to add, remove, and manipulate whitespace.

  3. Locale sensitive operations whose operations will vary from locale to locale.

  4. Pattern matching functions. These recognise four engines of pattern description. The most common is regular expressions, but there are three other tools.

Getting and setting individual characters

You can get the length of the string with str_length():

str_length("abc")
#> [1] 3

This is now equivalent to the base R function nchar(). Previously it was needed to work around issues with nchar() such as the fact that it returned 2 for nchar(NA). This has been fixed as of R 3.3.0, so it is no longer so important.

You can access individual character using str_sub(). It takes three arguments: a character vector, a start position and an end position. Either position can either be a positive integer, which counts from the left, or a negative integer which counts from the right. The positions are inclusive, and if longer than the string, will be silently truncated.

x <- c("abcdef", "ghifjk")

# The 3rd letter
str_sub(x, 3, 3)
#> [1] "c" "i"

# The 2nd to 2nd-to-last character
str_sub(x, 2, -2)
#> [1] "bcde" "hifj"

You can also use str_sub() to modify strings:

str_sub(x, 3, 3) <- "X"
x
#> [1] "abXdef" "ghXfjk"

To duplicate individual strings, you can use str_dup():

str_dup(x, c(2, 3))
#> [1] "abXdefabXdef"       "ghXfjkghXfjkghXfjk"

Whitespace

Three functions add, remove, or modify whitespace:

  1. str_pad() pads a string to a fixed length by adding extra whitespace on the left, right, or both sides.

    x <- c("abc", "defghi")
    str_pad(x, 10) # default pads on left
    #> [1] "       abc" "    defghi"
    str_pad(x, 10, "both")
    #> [1] "   abc    " "  defghi  "

    (You can pad with other characters by using the pad argument.)

    str_pad() will never make a string shorter:

    str_pad(x, 4)
    #> [1] " abc"   "defghi"

    So if you want to ensure that all strings are the same length (often useful for print methods), combine str_pad() and str_trunc():

    x <- c("Short", "This is a long string")
    
    x %>% 
      str_trunc(10) %>% 
      str_pad(10, "right")
    #> [1] "Short     " "This is..."
  2. The opposite of str_pad() is str_trim(), which removes leading and trailing whitespace:

    x <- c("  a   ", "b   ",  "   c")
    str_trim(x)
    #> [1] "a" "b" "c"
    str_trim(x, "left")
    #> [1] "a   " "b   " "c"
  3. You can use str_wrap() to modify existing whitespace in order to wrap a paragraph of text, such that the length of each line is as similar as possible.

    jabberwocky <- str_c(
      "`Twas brillig, and the slithy toves ",
      "did gyre and gimble in the wabe: ",
      "All mimsy were the borogoves, ",
      "and the mome raths outgrabe. "
    )
    cat(str_wrap(jabberwocky, width = 40))
    #> `Twas brillig, and the slithy toves did
    #> gyre and gimble in the wabe: All mimsy
    #> were the borogoves, and the mome raths
    #> outgrabe.

Locale sensitive

A handful of stringr functions are locale-sensitive: they will perform differently in different regions of the world. These functions are case transformation functions:

x <- "I like horses."
str_to_upper(x)
#> [1] "I LIKE HORSES."
str_to_title(x)
#> [1] "I Like Horses."

str_to_lower(x)
#> [1] "i like horses."
# Turkish has two sorts of i: with and without the dot
str_to_lower(x, "tr")
#> [1] "ı like horses."

String ordering and sorting:

x <- c("y", "i", "k")
str_order(x)
#> [1] 2 3 1

str_sort(x)
#> [1] "i" "k" "y"
# In Lithuanian, y comes between i and k
str_sort(x, locale = "lt")
#> [1] "i" "y" "k"

The locale always defaults to English to ensure that the default behaviour is identical across systems. Locales always include a two letter ISO-639-1 language code (like “en” for English or “zh” for Chinese), and optionally a ISO-3166 country code (like “en_UK” vs “en_US”). You can see a complete list of available locales by running stringi::stri_locale_list().

Pattern matching

The vast majority of stringr functions work with patterns. These are parameterised by the task they perform and the types of patterns they match.

Tasks

Each pattern matching function has the same first two arguments, a character vector of strings to process and a single pattern to match. stringr provides pattern matching functions to detect, locate, extract, match, replace, and split strings. I’ll illustrate how they work with some strings and a regular expression designed to match (US) phone numbers:

strings <- c(
  "apple", 
  "219 733 8965", 
  "329-293-8753", 
  "Work: 579-499-7527; Home: 543.355.3679"
)
phone <- "([2-9][0-9]{2})[- .]([0-9]{3})[- .]([0-9]{4})"

Engines

There are four main engines that stringr can use to describe patterns:

Fixed matches

fixed(x) only matches the exact sequence of bytes specified by x. This is a very limited “pattern”, but the restriction can make matching much faster. Beware using fixed() with non-English data. It is problematic because there are often multiple ways of representing the same character. For example, there are two ways to define “á”: either as a single character or as an “a” plus an accent:

a1 <- "\u00e1"
a2 <- "a\u0301"
c(a1, a2)
#> [1] "á" "á"
a1 == a2
#> [1] FALSE

They render identically, but because they’re defined differently, fixed() doesn’t find a match. Instead, you can use coll(), explained below, to respect human character comparison rules:

str_detect(a1, fixed(a2))
#> [1] FALSE
str_detect(a1, coll(a2))
#> [1] TRUE

Boundary

boundary() matches boundaries between characters, lines, sentences or words. It’s most useful with str_split(), but can be used with all pattern matching functions:

x <- "This is a sentence."
str_split(x, boundary("word"))
#> [[1]]
#> [1] "This"     "is"       "a"        "sentence"
str_count(x, boundary("word"))
#> [1] 4
str_extract_all(x, boundary("word"))
#> [[1]]
#> [1] "This"     "is"       "a"        "sentence"

By convention, "" is treated as boundary("character"):

str_split(x, "")
#> [[1]]
#>  [1] "T" "h" "i" "s" " " "i" "s" " " "a" " " "s" "e" "n" "t" "e" "n" "c" "e" "."
str_count(x, "")
#> [1] 19