Skip to main content
v2026.11,858 entries · CC-BY 4.0

substr in R: Extract and Replace Substrings

How substr() works in R: extract and replace by position, the edge-case rules, and when to use substring() or stringr str_sub() instead.

Written and maintained by CASRAI Editorial Board

Last updated

substr() returns the part of a character string between a start and a stop position, and the same function on the left of an assignment replaces that part. It is in base R, so nothing needs installing. The R manual defines it as substr(x, start, stop), with substring() as a vectorised sibling and stringr::str_sub() as the tidyverse equivalent.

What substr() is used for in research

Research datasets often pack information into fixed-position text: an identifier whose first characters name a site, a code whose last digits give a sequence number, a date string stored as text. substr() pulls those pieces apart without regular expressions. The example below uses an invented identifier, AB1234, purely for illustration.

id <- "AB1234"
substr(id, 1, 2)   # "AB"
substr(id, 3, 6)   # "1234"

Once the pieces are separate you can tidy them with the other string helpers covered in our guides on lower case in R, upper case in R and concatenating strings in R, or store the results as new columns in a data frame.

Install or access

substr() and substring() belong to the base package, which is loaded in every R session, so there is nothing to install for them. str_sub() comes from the add-on package stringr, which is distributed through CRAN like other R packages:

install.packages("stringr")
library(stringr)

Step-by-step: extract, replace and vectorise

1. Extract a substring

The usage is substr(x, start, stop), where x is a character vector and start and stop are integer positions. The manual’s own example, substr("abcdef", 2, 4), returns the characters from position 2 to position 4, which is "bcd". Non-character input is converted with as.character first.

substr("abcdef", 2, 4)   # "bcd"

2. Replace part of a string

Use the replacement form substr(x, start, stop) <- value. If the portion being replaced is longer than value, only as many characters as value provides are replaced, so the string does not grow or shrink. If start is past the end of the string, nothing is replaced.

x <- "abcdef"
substr(x, 2, 5) <- "Q"   # x is now "aQcdef"

3. Work on a whole vector

substr() returns a result with the same length and attributes as x, so it can be applied to a column of identifiers in one call. substring(text, first, last = NULL) follows the S-language convention of first and last instead of start and stop, recycles its vector arguments to the longest, and returns a result as long as the longest argument. That makes it the tool for cutting one string at many positions:

substring("abcdef", 1:6, 1:6)   # "a" "b" "c" "d" "e" "f"
substring("abcdef", 2)          # "bcdef" (last defaults to NULL, so it runs to the end)

4. Count from the end with stringr

In str_sub(string, start = 1L, end = -1L), positive values count from the left and negative values count from the right, so -1 is the last character. The stringr reference gives these results for hw <- "Hadley Wickham":

str_sub(hw, 1, 6)     # "Hadley"
str_sub(hw, end = -7) # "Hadley W"
str_sub(hw, -7)       # "Wickham"
str_sub(hw, -1)       # "m"

start and end can also be a two-column matrix, for example the output of str_locate(), and a replacement form str_sub(x, start, end) <- value exists. The R manual page for base substr() does not document negative positions, so use str_sub() when you need to count from the right.

Output and how to read it

The edge cases are where most surprises come from. The R manual documents the base rules, and str_sub() has its own:

Situation base substr() / substring() stringr str_sub()
Start is past the end of the string Returns an empty string Returns an empty string
Stop is past the end of the string Result runs to the end of the string Not documented on the reference page
End is before start Not documented on the manual page Returns an empty string
An argument is NA The matching element of the answer is NA Controlled by omit_na in the replacement form: if TRUE, missing values in the arguments leave the input unchanged
Last/stop omitted substring(): runs to the end of each string end = -1 by default, the last character
Replacement shorter than the target Only as many characters as the replacement provides are replaced Not documented on the reference page

An empty string "" is a valid result, not an error, and it is different from NA. If a column of extracted codes shows blanks, the start position is past the end of those strings.

Troubleshooting

The result is blank

The start position is beyond the length of that string. Check lengths with nchar() before choosing positions.

The result is NA

If any argument has an NA element, the corresponding element of the answer is NA, including a missing value in x itself.

Wrong characters from non-English text

The manual says declared Latin-1 or UTF-8 encoding is kept when the locale matches, while strings marked with the "bytes" encoding are subset by bytes rather than characters. When truncating strings it recommends nchar(type = "chars") to count characters, and strtrim when the goal is display width.

The replacement did not change the length

The replacement form overwrites characters in place: if the portion being replaced is longer than the new value, only as many characters as the value provides are replaced.

Free alternatives

Function Package Notable feature (per the documentation)
substr() base R Positions as start and stop; result keeps the length and attributes of x; has a replacement form
substring() base R Arguments recycled to the longest; last defaults to NULL (end of string); has a replacement form
str_sub() stringr Negative positions count from the right; accepts a start/end matrix from str_locate(); str_sub_all() returns all pieces for each string

FAQ

How do I get the last n characters of a string in R?

With stringr, use str_sub(x, -n): negative values count from the right, so str_sub(hw, -7) returns "Wickham" from "Hadley Wickham". The base R manual page for substr() does not document negative positions, so it gives no equivalent shortcut.

What is the difference between substr() and substring()?

Both extract by position. substring() uses the argument names first and last, defaults last to NULL so it runs to the end of each string, and recycles vector arguments to the longest, so one string can be cut at many positions in one call.

Does substr() change the original string?

Extraction returns a new value. The replacement form substr(x, start, stop) <- value modifies x overwriting characters in place.

Sources

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about substr in R: Extract and Replace Substrings

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →