Strings & Regular Expressions
Real data is full of text: names, addresses, phone numbers, product codes, comments. Text values are called strings (or character vectors) in R. You will need to join them, format numbers inside them, cut them apart, and clean messy entries such as "98765-43210" and "+91 99887 76655" into one standard form.
In this lesson you will build and format strings, use the main text functions, and learn regular expressions, a mini-language for describing text patterns.
Joining and formatting text
paste() joins values with a space, paste0() joins them with nothing in between, and sprintf() fills a template:
name <- "Ananya"
city <- "Kochi"
paste("Hello", name, "from", city)
paste0("ORD-", 1:3)
paste(c("Tea", "Coffee", "Juice"), collapse = ", ")
sprintf("%s paid Rs %.2f", name, 1299.5)
sprintf("Roll no: %03d", c(7, 42, 115))
sprintf("%-10s|%6.1f", c("Pune", "Lucknow"), c(31.4, 28))
format(1234567, big.mark = ",")[1] "Hello Ananya from Kochi" [1] "ORD-1" "ORD-2" "ORD-3" [1] "Tea, Coffee, Juice" [1] "Ananya paid Rs 1299.50" [1] "Roll no: 007" "Roll no: 042" "Roll no: 115" [1] "Pune | 31.4" "Lucknow | 28.0" [1] "1,234,567"
paste()andpaste0()are vectorised:paste0("ORD-", 1:3)makes three order numbers.collapsejoins all the elements of one vector into a single string.- In
sprintf(),%sis a placeholder for text,%dfor a whole number and%ffor a decimal.%.2fmeans two decimal places,%03dpads with zeros to width 3,%6.1fuses width 6 with one decimal, and%-10sleft-aligns text in width 10. Widths line up columns in reports. format()withbig.markadds thousands separators. It groups by threes, so Indian lakh grouping (12,34,567) needs your own code.
Everyday text functions
station <- " New Delhi Railway Station "
trimws(station)
toupper(trimws(station))
nchar(trimws(station))
substr("Rohtak", 1, 3)
parts <- strsplit("Pune,Kochi,Jaipur", ",")
parts
parts[[1]][2]
startsWith(c("9876543210", "8123456789"), "98")[1] "New Delhi Railway Station" [1] "NEW DELHI RAILWAY STATION" [1] 25 [1] "Roh" [[1]] [1] "Pune" "Kochi" "Jaipur" [1] "Kochi" [1] TRUE FALSE
| Function | Does |
|---|---|
trimws(x) | Removes spaces at the start and end |
toupper(x), tolower(x) | Changes case |
nchar(x) | Counts characters |
substr(x, start, stop) | Cuts out characters by position |
strsplit(x, split) | Splits text into pieces; returns a list |
startsWith(x, prefix), endsWith(x, suffix) | Checks the beginning or end |
strsplit() returns a list because each input string may split into a different number of pieces. Use [[1]] to get the pieces of the first string.
Regular expressions
A regular expression (regex) describes a pattern instead of fixed text. The search functions grepl(), sub() and gsub() all accept one:
| Pattern | Matches |
|---|---|
[0-9] | Any one digit |
[^0-9] | Any one character that is not a digit |
[6-9] | One digit from 6 to 9 |
{10} | The previous item exactly 10 times |
^ and $ | The start and the end of the text |
? | The previous item is optional |
. | Any one character |
( ) | A group, which you can reuse as \\1 in a replacement |
Here is a real cleaning job: phone numbers typed in many styles. The goal is ten digits starting with 6 to 9, the pattern used by Indian mobile numbers:
phones <- c("98765-43210", "call 9123456780", "12345", "+91 99887 76655")
digits <- gsub("[^0-9]", "", phones)
digits
grepl("^(91)?[6-9][0-9]{9}$", digits)
sub("^91([6-9][0-9]{9})$", "\\1", digits)[1] "9876543210" "9123456780" "12345" "919988776655" [1] TRUE TRUE FALSE TRUE [1] "9876543210" "9123456780" "12345" "9988776655"
gsub("[^0-9]", "", phones)replaces every non-digit with nothing. (sub()would replace only the first match.)grepl()returnsTRUEwhere the pattern matches: an optional91country code, then a digit from 6 to 9, then nine more digits, with nothing else before or after.sub()with a group removes a leading91only when ten valid digits follow it.\\1puts back what the brackets matched. A carelesssub("^91", "", digits)would also damage "9123456780", a normal number that happens to start with 91.
In R strings, a backslash must be written twice, so the regex \d (a digit) is written "\\d".
Searching and extracting
log <- c("Order 1042 paid Rs 450", "Order 1043 paid Rs 1299", "Order 1044 cancelled")
grep("paid", log)
grepv("paid", log)
amounts <- regmatches(log, regexpr("Rs [0-9]+", log))
amounts
as.numeric(sub("Rs ", "", amounts))
grepl("PAID", log, ignore.case = TRUE)
grepl(".", "Rs 450", fixed = TRUE)[1] 1 2 [1] "Order 1042 paid Rs 450" "Order 1043 paid Rs 1299" [1] "Rs 450" "Rs 1299" [1] 450 1299 [1] TRUE TRUE FALSE [1] FALSE
grep()returns the positions of matching elements, andgrepv()returns the matching values.regexpr()finds the first match in each string andregmatches()extracts it.[0-9]+means "one or more digits".ignore.case = TRUEignores capital letters.fixed = TRUEtreats the pattern as plain text, so.means a real dot, not "any character". Use it whenever you search for text that contains symbols.
Raw strings for paths and patterns
Since R 4.0.0, a raw string r"(...)" keeps backslashes exactly as typed, which is handy for Windows paths and complex regex:
path <- r"(C:\Users\priya\data\sales.csv)"
path
writeLines(path)
writeLines("Name:\tMeera\nCity:\tPune")[1] "C:\\Users\\priya\\data\\sales.csv" C:\Users\priya\data\sales.csv Name: Meera City: Pune
When R prints a string, it shows escape sequences, so each backslash appears doubled. writeLines() shows the real text. In a normal string, \t is a tab and \n a new line. In R you can also simply write Windows paths with forward slashes: "C:/Users/priya/data/sales.csv" works too.
Common mistakes
- Using
sub()when you meantgsub().sub()changes only the first match in each string. - Forgetting that
.is special in regex.grepl(".", x)matches any character; usefixed = TRUEor"\\.". - Single backslashes.
"\d"is an error in R; write"\\d"or use a raw string. - Forgetting that
strsplit()returns a list. Use[[1]]orunlist().
Key points
paste(),paste0()andsprintf()build strings;format()adds separators.trimws(),toupper(),nchar(),substr()andstrsplit()handle everyday text tasks.grepl()tests,grep()/grepv()find, andsub()/gsub()replace using regular expressions.- Anchors
^and$, classes like[0-9], counts like{10}and groups( )describe patterns. - Write backslashes twice in normal strings, or use raw strings
r"(...)".
Exercise
A college form collected email addresses with messy spacing and capitals: " Priya.S@Example.com ", "rohan@college.ac.in", "kabir at gmail.com" and "MEERA@KOCHI.ORG ". Clean them (trim spaces, convert to lower case), flag which ones look valid with a simple pattern (some characters, @, some characters, a dot, letters), and print each valid address with sprintf() as "OK: address".
Show solution
emails <- c(" Priya.S@Example.com ", "rohan@college.ac.in",
"kabir at gmail.com", "MEERA@KOCHI.ORG ")
clean <- tolower(trimws(emails))
valid <- grepl("^[^@ ]+@[^@ ]+\\.[a-z]+$", clean)
valid
sprintf("OK: %s", clean[valid])[1] TRUE TRUE FALSE TRUE [1] "OK: priya.s@example.com" "OK: rohan@college.ac.in" [3] "OK: meera@kochi.org"
[^@ ]+ means "one or more characters that are not @ or a space". The third address has no @, so it fails. Real email validation is more complex; this simple check catches common typing mistakes.
Quick Quiz
1. What does paste0("A", 1:2) return?
paste0() joins without a separator and is vectorised.
2. What is the difference between sub() and gsub()?
The "g" stands for global: every match is replaced.
3. Which pattern matches text that is exactly six digits?
{6} repeats the digit class six times, and ^/$ stop anything else from appearing.
4. What does sprintf("%.1f", 3.14159) return?
%.1f formats a number with one decimal place.
Learn R Programming with a mentor
Free lessons get you started. Our Data Science & Machine Learning training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
