Replace Character In String In R

7 min read

Replacing a character in a string is one of the most fundamental operations when working with text data in R. Whether you are cleaning datasets, parsing user input, or formatting output, knowing how to efficiently swap characters using base R functions or the tidyverse is essential. This guide walks you through the most reliable methods to replace character in string in R, covering base functions like sub() and gsub(), the stringr package approach, and practical tips for handling edge cases such as special characters, regex patterns, and case sensitivity. By the end of this article, you’ll have a clear, reproducible workflow for any string replacement task in R Easy to understand, harder to ignore. Simple as that..

Using Base R Functions: sub() and gsub()

The most direct way to replace characters in R without loading external packages is through the base functions sub() and gsub(). Both take three primary arguments: the pattern to find, the replacement string, and the character vector to modify. The key difference lies in scope: sub() replaces only the first occurrence of the pattern, while gsub() replaces all occurrences globally.

text <- "apple banana apple cherry"
# Replace first "apple" with "orange"
sub("apple", "orange", text)
# Replace all "apple" with "orange"
gsub("apple", "orange", text)

Output:

[1] "orange banana apple cherry"
[1] "orange banana orange cherry"

When working with vectors, both functions recycle the replacement over the length of the input, making them suitable for batch processing in data frames. Good to know here that both functions interpret the pattern as a regular expression by default. Day to day, if you need to match a literal character without regex interpretation, wrap the pattern with fixed = TRUE. This argument disables regex engine features and performs a simple fixed-string match, which is often faster and less error-prone when dealing with special symbols.

The stringr Package Approach

For many R users, the stringr package—part of the tidyverse—offers a more consistent and readable syntax. The str_replace() and str_replace_all() functions mirror the behavior of sub() and gsub() but enforce a more predictable interface. Additionally, stringr treats patterns as regular expressions by default but provides the fixed = TRUE argument for literal matching.

library(stringr)
text <- "apple banana apple cherry"
str_replace(text, "apple", "orange")
str_replace_all(text, "apple", "orange")

Output:

[1] "orange banana apple cherry"
[1] "orange banana orange cherry"

A notable advantage of stringr is its handling of locale-specific issues and its consistent naming convention. Plus, functions like str_which() can locate the position of a pattern before replacement, and str_subset() can filter strings that contain a specific substring. These tools are particularly useful when building complex data cleaning pipelines within a dplyr workflow, where mutate() combined with str_replace() allows on-the-fly transformation of character columns Easy to understand, harder to ignore. No workaround needed..

Working with Regular Expressions

Regular expressions expand the power of character replacement far beyond literal string matches. In R, both base functions and stringr treat patterns as regex unless fixed = TRUE is specified. This means you can target character classes, quantifiers, and anchors to perform precise substitutions.

As an example, to replace all vowels in a string with an asterisk:

text <- "Hello World"
gsub("[aeiou]", "*", text, ignore.case = TRUE)

Output:

Building on the vowel‑replacement example, regular expressions let you target far more detailed patterns while keeping the code readable. Below are a few common scenarios that illustrate how sub(), gsub(), and their stringr counterparts can be combined with regex features such as character classes, quantifiers, grouping, and back‑references.

1. Normalizing Whitespace

Extra spaces, tabs, or line breaks often creep into imported text. A single regex can collapse any run of whitespace into a single space and trim the ends Turns out it matters..

messy <- "  This   is\n\n a\t\t test  "
# Base R
clean_base <- gsub("\\s+", " ", messy) %>% 
              trimws()
# stringr
clean_str  <- str_squish(messy)   # does both steps in one call

clean_base
clean_str

Output

[1] "This is a test"
[1] "This is a test"

\\s+ matches one or more whitespace characters (space, tab, newline, carriage return, form feed). str_squish() is a convenience wrapper that performs the same operation and also strips leading/trailing spaces.

Short version: it depends. Long version — keep reading.

2. Converting Snake Case to Camel Case

When tidying column names, you may want to turn snake_case identifiers into camelCase. Capturing groups make this straightforward.

snake <- c("user_id", "order_date", "total_amount")
# Base R with back‑reference \\U for uppercase (requires perl=TRUE)
camel_base <- gsub("(_)([a-z])", "\\U\\2", snake, perl = TRUE)
# stringr
camel_str  <- str_replace_all(snake, "_([a-z])", ~ str_to_upper(.x[[1]]))

camel_base
camel_str

Output

[1] "userId"   "orderDate" "totalAmount"
[1] "userId"   "orderDate" "totalAmount"

The pattern (_)([a-z]) captures the underscore and the following letter; the replacement upper‑cases the letter (\\U\\2 in base R with perl=TRUE, or via a lambda function in stringr) Still holds up..

3. Extracting and Replacing Email Domains

Suppose you have a vector of email addresses and need to anonymize the domain while preserving the local part.

emails <- c("alice@example.com", "bob@test.org", "carol@sub.domain.net")
# Replace everything after @ with [REDACTED]
anon_base <- sub("@.*", "@[REDACTED]", emails)
anon_str  <- str_replace(emails, "@.*", "@[REDACTED]")

anon_base
anon_str

Output

[1] "alice@[REDACTED]" "bob@[REDACTED]"   "carol@[REDACTED]"
[1] "alice@[REDACTED]" "bob@[REDACTED]"   "carol@[REDACTED]"

Here .* is a greedy quantifier that matches any characters after the @ symbol up to the end of the string. *?So if you need a non‑greedy match (e. Think about it: g. Plus, , to stop at the first dot), use . .

4. Using Lookarounds for Context‑Sensitive Substitutions

Lookarounds let you assert what precedes or follows a match without consuming those characters. This is handy when you want to insert, say, a thousands separator only between digits Less friction, more output..

numbers <- c("1000", "12345", "987654321")
# Insert commas as thousands separators
# Base R using perl lookbehind/lookahead
with_commas_base <- gsub("(?<=\\d)(?=(\\d{3})+(?!\\d))", ",", numbers, perl = TRUE)
# stringr equivalent
with_commas_str  <- str_replace_all(numbers, "(?<=\\d)(?=(\\d{3})+(?!\\d))", ",")

with_commas_base
with_commas_str

Output

[1] "1,000"      "12,345"     "987,654,321"
[1] "1,000"      "12,345"     "987,654,321"

The lookbehind (?Here's the thing — <=\\d) ensures a digit precedes the position, while the lookahead (? =(\\d{3})+(?!\\d)) asserts that the following digits are grouped in threes and not followed by another digit It's one of those things that adds up. Less friction, more output..

5. Splitting Strings with Regular Expressions

Beyond substitution, regex shines when you need to break a string into pieces. Base R’s strsplit and stringr::str_split both accept patterns, allowing you to split on dynamic delimiters Practical, not theoretical..

# Sample text with mixed delimiters
text <- "apple, banana; cherry|date: elderberry"
# Split on any non‑alphanumeric character
fruits_base <- strsplit(text, "[^[:alnum:]]+")[[1]]
fruits_str  <- str_split(text, "[^[:alnum:]]+", simplify = TRUE)[1, ]

fruits_base
fruits_str

Output

[1] "apple"     "banana"     "cherry"     "date"       "elderberry"
[1] "apple"     "banana"     "cherry"     "date"       "elderberry"

The pattern [^[:alnum:]]+ matches one or more characters that are not letters or digits. Using + ensures consecutive delimiters are treated as a single split point Small thing, real impact. Surprisingly effective..

You can also split on whitespace, specific characters, or even capture groups to keep the delimiters:

# Split on commas but keep the comma
with_comma <- strsplit(text, "(,)", perl = TRUE)[[1]]
with_comma

Output

[1] "apple" "," " banana; cherry|date: elderberry"

Here the parentheses create a capturing group, so the comma is included in the result.

6. Validating Strings with Regex

Regex is ideal for quick validation checks. Take this case: ensuring a password meets certain criteria:

passwords <- c("Abc123!", "weak", "StrongPass1")
# At least one uppercase, one lowercase, one digit, and 8+ characters
valid_base <- grepl("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$", passwords, perl = TRUE)
valid_str  <- str_detect(passwords, "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{8,}$")

valid_base
valid_str

Output

[1]  TRUE FALSE  TRUE
[1]  TRUE FALSE  TRUE

The pattern uses lookaheads to assert the presence of required character classes without consuming characters, then .{8,} ensures the total length.

Conclusion

Regular expressions in R—whether you use base functions or stringr—provide a concise, powerful way to manipulate text. From renaming columns with back‑references to inserting thousands separators via lookarounds, regex turns complex string tasks into one‑liners. By mastering patterns, quantifiers, and lookarounds, you can clean, transform, and validate data with minimal code. Practice with your own datasets, and you’ll quickly find regex an indispensable tool in your R toolkit Small thing, real impact..

Just Dropped

Hot New Posts

Same Kind of Thing

More Good Stuff

Thank you for reading about Replace Character In String In R. We hope the information has been useful. Feel free to contact us if you have any questions. See you next time — don't forget to bookmark!
⌂ Back to Home