Code
nchar("hello")[1] 5
通过本章学习,你将能够:
paste() 和 paste0() 拼接字符串strsplit() 拆分和 trimws() 清洗字符串sprintf() 进行格式化输出grep()、sub()、gsub() 进行模式匹配与替换regexpr() + regmatches() 提取匹配内容你已经见过 nchar():
nchar("hello")[1] 5
👉 nchar() 返回字符数;type = "bytes" 返回字节数(中文字符通常占 2-3 个字节)。
nchar() 和 length() 是两回事:
x <- c("a", "abc", "abcde")
length(x) # 3 —— 向量里有几个元素[1] 3
nchar(x) # 1 3 5 —— 每个元素有几个字符[1] 1 3 5
👉 一个问”向量多长”,一个问”每个字符串多长”。不要搞混。
substr(x, start, stop) 按位置截取子串:
substr("hello", 2, 4) # "ell"[1] "ell"
substr() 还能用于原地修改:
x <- "hello"
substr(x, 1, 1) <- "H"
x # "Hello"[1] "Hello"
toupper("hello") # "HELLO"[1] "HELLO"
tolower("HELLO") # "hello"[1] "hello"
casefold("HELLO") # "hello"(默认 upper = FALSE)[1] "hello"
casefold("hello", upper = TRUE) # "HELLO"[1] "HELLO"
chartr() 可以按字符一一对应地翻译:
chartr("a-z", "A-Z", "hello") # "HELLO"[1] "HELLO"
chartr("aeiou", "AEIOU", "hello") # "hEllO"[1] "hEllO"
paste() 和 paste0() 是 R 中最常用的拼接函数:
paste("A", "B", "C") # "A B C"(默认空格分隔)[1] "A B C"
paste("A", "B", sep = "-") # "A-B"[1] "A-B"
paste0("A", "B", "C") # "ABC"(等价于 paste(..., sep = ""))[1] "ABC"
👉 记住:paste0() = paste(sep = ""),不带分隔符。
向量化拼接——不同长度的向量会自动循环:
paste("sample", 1:3, sep = "_") # "sample_1" "sample_2" "sample_3"[1] "sample_1" "sample_2" "sample_3"
collapse 参数把向量折叠成单个字符串:
paste(c("A", "B", "C"), collapse = ", ") # "A, B, C"[1] "A, B, C"
paste(letters[1:5], collapse = "-") # "a-b-c-d-e"[1] "a-b-c-d-e"
👉 sep 是多个参数之间的分隔符,collapse 是把向量元素合并成一个。
strsplit() 按分隔符拆分,返回列表:
strsplit("a,b,c", split = ",") # 返回列表[[1]]
[1] "a" "b" "c"
strsplit("a,b,c", split = ",")[[1]] # c("a", "b", "c")[1] "a" "b" "c"
👉 为什么返回列表?因为拆分后每段长度可能不同,只有列表能容纳。
# 拆分为单个字符
strsplit("abcdef", split = "")[[1]]
[1] "a" "b" "c" "d" "e" "f"
# 向量化拆分
strsplit(c("a,b", "c,d,e"), split = ",")[[1]]
[1] "a" "b"
[[2]]
[1] "c" "d" "e"
# unlist 拉平
unlist(strsplit(c("a,b", "c,d,e"), split = ","))[1] "a" "b" "c" "d" "e"
trimws() 去除首尾空白:
trimws(" hello ") # "hello"[1] "hello"
trimws(" hello ", which = "left") # "hello "[1] "hello "
trimws(" hello ", which = "right") # " hello"[1] " hello"
清洗数据框中的字符列:
df <- data.frame(name = c(" 张三 ", " 李四 "), score = 1:2)
df$name <- trimws(df$name)
df name score
1 张三 1
2 李四 2
sprintf() 用格式占位符生成指定格式的字符串:
sprintf("编号%03d", 7) # "编号007"(零填充到3位)[1] "编号007"
sprintf("%.2f", 3.14159) # "3.14"(两位小数)[1] "3.14"
sprintf("%s:%d", "样本", 1:3) # "样本:1" "样本:2" "样本:3"[1] "样本:1" "样本:2" "样本:3"
sprintf("%10s", "abc") # " abc"(右对齐,宽度10)[1] " abc"
sprintf("%-10s", "abc") # "abc "(左对齐,宽度10)[1] "abc "
sprintf("%05d", 42) # "00042"(整数零填充)[1] "00042"
常用格式符:
| 格式符 | 含义 |
|---|---|
%s |
字符串 |
%d |
整数 |
%f |
浮点数 |
%e |
科学计数法 |
for (i in 1:10) {
filename <- sprintf("Fig_%02d.png", i)
png(filename, width = 800, height = 600)
x <- 1:20
y <- rnorm(20, mean = i * 2, sd = 3)
plot(x, y, type = "b", pch = 16, col = i + 1, lwd = 2,
main = paste0("Figure_", i),
xlab = "x", ylab = "y")
dev.off()
}grep() 返回匹配元素的索引,grepl() 返回逻辑向量:
x <- c("apple", "banana", "grape", "pear", "apricot")
grep("^a", x) # 1 5(索引)[1] 1 5
grep("^a", x, value = TRUE) # "apple" "apricot"(值)[1] "apple" "apricot"
grepl("^a", x) # TRUE FALSE FALSE FALSE TRUE[1] TRUE FALSE FALSE FALSE TRUE
👉 grepl() 返回逻辑向量,可以直接用于 [ ] 筛选或 subset()。
# 逻辑向量筛选
x[grepl("a", x)] # 所有含 a 的元素[1] "apple" "banana" "grape" "pear" "apricot"
# 忽略大小写
grep("A", c("apple", "Apple"), ignore.case = TRUE)[1] 1 2
# 反向匹配
grep("a", x, invert = TRUE) # pear(不含 a 的元素)integer(0)
sub() 替换首次匹配,gsub() 替换全部匹配:
sub("a", "X", "banana") # "bXnana"(仅第一个)[1] "bXnana"
gsub("a", "X", "banana") # "bXnXnX"(全部)[1] "bXnXnX"
👉 工作中绝大多数情况用 gsub(),因为通常要把所有匹配都替换掉。
# 删除多余空格
gsub("\\s+", " ", "a b c") # "a b c"[1] "a b c"
# 向量化替换
fruits <- c("apple", "pear")
gsub("p", "P", fruits) # "aPPle" "Pear"[1] "aPPle" "Pear"
# 删除所有数字
gsub("\\d", "", "abc123def456") # "abcdef"[1] "abcdef"
regexpr() / gregexpr() 返回匹配位置,配合 regmatches() 提取匹配文本:
# 提取首次匹配
x <- "ID编号007"
m <- regexpr("\\d+", x)
regmatches(x, m) # "007"[1] "007"
# 提取全部匹配
x <- "a1b22c333"
m <- gregexpr("\\d+", x)
regmatches(x, m) # list("1", "22", "333")[[1]]
[1] "1" "22" "333"
# 提取邮箱域名(需要 perl = TRUE 支持后顾断言)
email <- "user@example.com"
m <- regexpr("(?<=@)\\S+", email, perl = TRUE)
regmatches(email, m) # "example.com"[1] "example.com"
# 批量提取
emails <- c("a@gmail.com", "b@163.com", "c@qq.com")
m <- gregexpr("(?<=@)\\S+", emails, perl = TRUE)
unlist(regmatches(emails, m)) # "gmail.com" "163.com" "qq.com"[1] "gmail.com" "163.com" "qq.com"
👉 perl = TRUE 启用 PCRE 引擎,支持后顾断言 (?<=...) 等高级特性。
R 默认使用 TRE 正则引擎,设置 perl = TRUE 可使用 PCRE 引擎。
| 模式 | 含义 | 示例 |
|---|---|---|
. |
任意单个字符 | a.b 匹配 “aab” |
^ |
行首 | ^a 以 a 开头 |
$ |
行尾 | a$ 以 a 结尾 |
\\d |
数字 [0-9] |
\\d+ 提取数字 |
\\w |
单词字符 | \\w+ 提取单词 |
\\s |
空白字符 | \\s+ 匹配连续空格 |
[abc] |
字符类 | a、b 或 c |
[^abc] |
否定字符类 | 非 a、b、c |
* |
零次或多次 | a* |
+ |
一次或多次 | a+ |
? |
零次或一次 | a? |
{n} |
恰好 n 次 | \\d{4} 4位数字 |
{n,m} |
n 到 m 次 | \\d{2,4} |
() |
捕获分组 | (\\d+)-(\\d+) |
| |
或 | apple\|banana |
grep("^\\d{3}-\\d{4}", c("010-1234", "01-1234")) # 匹配电话区号[1] 1
gsub("[[:punct:]]", "", "Hello, World!") # 去掉所有标点[1] "Hello World"
gsub("[^0-9]", "", "电话:138-1234-5678") # 只保留数字[1] "13812345678"
批量处理文件名——提取日期并转格式:
files <- c("report_20240115.csv", "data_20231201.pdf",
"summary_20240320.xlsx")
# 提取 8 位日期
m <- regexpr("\\d{8}", files)
dates <- regmatches(files, m)
dates # "20240115" "20231201" "20240320"[1] "20240115" "20231201" "20240320"
# 转为标准格式
gsub("(\\d{4})(\\d{2})(\\d{2})", "\\1-\\2-\\3", dates)[1] "2024-01-15" "2023-12-01" "2024-03-20"
# "2024-01-15" "2023-12-01" "2024-03-20"数据框清洗——统一清理字符列:
df <- data.frame(
name = c(" 张三 ", "李 四", "王五"),
phone = c("138-1234-5678", "(010)82345678", "13912345678"),
stringsAsFactors = FALSE
)
df$name <- trimws(df$name) # 去首尾空白
df$name <- gsub("\\s+", "", df$name) # 去内部空格
df$phone <- gsub("[^0-9]", "", df$phone) # 只保留数字
df name phone
1 张三 13812345678
2 李四 01082345678
3 王五 13912345678
文本分词与词频统计:
text <- c("R is a language for statistical computing",
"Python is a general purpose language")
words <- unlist(strsplit(tolower(text), " "))
freq <- sort(table(words), decreasing = TRUE)
head(freq, 8)words
a is language computing for general purpose python
2 2 2 1 1 1 1 1
nchar() 字符数,length() 元素数substr() / substring() 按位置提取,substr() 可赋值修改toupper()、tolower()、casefold();chartr() 字符翻译paste() / paste0(),sep 分隔参数,collapse 折叠向量strsplit(),返回列表,用 [[ ]] 或 unlist() 提取trimws() 去首尾空白sprintf() 用 %s、%d、%f 生成格式化字符串grep() 返回索引,grepl() 返回逻辑值sub() 换首个,gsub() 换全部regexpr() / gregexpr() + regmatches() 提取匹配内容👉 字符串处理是数据清洗的基本功。 打开 RStudio,把每个函数的例子都动手跑一遍。