第十章 使用stringr处理字符串
重点正则表达式——难!!!
提要:
stringr包主要函数函数
拼接
str_c: 字符串拼接。
str_join: 字符串拼接,同str_c。
str_trim: 去掉字符串的空格和TAB(\t)
str_pad: 补充字符串的长度
str_dup: 复制字符串
str_wrap: 控制字符串输出格式
str_sub: 截取字符串
str_sub<- 截取字符串,并赋值,同str_sub
计算
str_count: 字符串计数
str_length: 字符串长度
str_sort: 字符串值排序
str_order: 字符串索引排序,规则同str_sort
匹配
str_split: 字符串分割
str_split_fixed: 字符串分割,同str_split
str_subset: 返回匹配的字符串
word: 从文本中提取单词
str_detect: 检查匹配字符串的字符
str_match: 从字符串中提取匹配组。
str_match_all: 从字符串中提取匹配组,同str_match
str_replace: 字符串替换
str_replace_all: 字符串替换,同str_replace
str_replace_na:把NA替换为NA字符串
str_locate: 找到匹配的字符串的位置。
str_locate_all: 找到匹配的字符串的位置,同str_locate
str_extract: 从字符串中提取匹配字符
str_extract_all: 从字符串中提取匹配字符,同str_extract
变换
str_conv: 字符编码转换
str_to_upper: 字符串转成大写
str_to_lower: 字符串转成小写,规则同str_to_upper
str_to_title: 字符串转成首字母大写,规则同str_to_upper
1.准备工作
library(tidyverse)
library(stringr)
2.字符串基础
单引号和双引号没有区别
转义符号\,对于反斜杠和引号需要转义。
stringl<-"This is a string"
string2<-'To put a "quote"inside a string,use single quotes'
打印形式中会显示出转义字符
x<-c("\"","\\")
x
# [1] "\"" "\\"
如果想要查看字符串的初始内容,可以使用writelines()函数:
writeLines(x)
# "
# \
几种特殊字符。最常用的是换行符\n和制表符\t
非英文字符的写法
x<-"\u00b5"
x
# [1] "µ"
2.1 字符串的长度——str_length
stringr中的函数名称更直观,都是以str_开头的。
例如,str_length()函数可以返回字符串中的字符数量:
str_length(c("a","R for data science",NA))
# [1] 1 18 NA
2.2 字符串组合——str_c 类似 paste
str_c("x","y")
#>[1]"xy"
str_c("x","y","z")
#>[1]"xyz"
可以使用sep参数来控制字符串间的分隔方式:
str_c("x","y",sep="-")
[1] "x-y"
str_c(letters[1:5],1:5,sep="-")
# [1] "a-1" "b-2" "c-3" "d-4" "e-5"
要想将字符向量合并为字符串,可以使用collapse参数:
对比
str_c(letters[1:5],1:5,sep="-")
# [1] "a-1" "b-2" "c-3" "d-4" "e-5"
#collapse 参数按输入的分隔符整合为一个字符串
str_c(letters[1:5],1:5,sep="-",collapse = "★")
# [1] "a-1★b-2★c-3★d-4★e-5"
如果想要将它们输出为"NA",可以使用str_replace_na():
x<-c("abc",NA)
str_c("|-",x,"-|")
# [1] "|-abc-|" NA
str_c("|-",str_replace_na(x),"-|")
[1] "|-abc-|" "|-NA-|"
str_c()函数是向量化的,它可以自动循环短向量
str_c("prefix-",c("a","b","c"),"-suffix")
# [1] "prefix-a-suffix" "prefix-b-suffix" "prefix-c-suffix"
2.3 字符串取子集——str_sub() 函数来提取字符串的一部分
x<-c("AppLe","Banana","Pear")
# 对向量中每一个字符串都进行了提取
str_sub(x,start = 1,end = 3)#正方向提取
# [1] "App" "Ban" "Pea"
str_sub(x,start = -3,end = -1)#负方向提取
# [1] "pLe" "ana" "ear"
注意:即使字符串过短,str_sub函数也不会出错,它将返回尽可能多的字符:
str_sub("a",1,5)
# [1] "a"
还可以使用str_sub()函数的赋值形式来修改字符串:
比如将首字母转换为小写
str_sub(x,1,1)<-str_to_lower(str_sub(x,1,1))
x
# [1] "appLe" "banana" "pear"
2.4 区域设置——不同语言的转换大小写和排序
str_to_upper(c("i","1"))
#>[1]"T""I"
str_to_upper(c("i","1"),locale="tr")
#>[1]"t""I"
str_to_lower() 大写变小写
str_to_upper() 小写变大写
str_to_title() 首字母大写
str_sort()
str_order()
3.使用正则表达式进行模式匹配
通过str_view() 和str_view_all() 函数来学习正则表达式。这两个函数接受一个字符向量和一个正则表达式,并显示出它们是如何匹配的。
3.1 基础匹配
精确匹配字符串:
x <- c("apple", "banana", "pear")
str_view(x, "an")
用"."——点——任意字符匹配(除了换行符):
str_view(x, ".a.")
如果需要匹配”.“需要用正则表达式转义:正则表达式 \. 的字符串形式应是 \\.
那么如何匹配 \ 这个字符呢?
这意味着要想匹配字符\,我们需要输入"\\\\"----需要4个反斜杠来匹配1个反斜杠!
3.3 锚点–设置锚点,以便R从字符串的开头或末尾进行匹配
记忆:始于权力(^),终于金钱($)
强制正则表达式匹配一个完整字符串。
3.5 字符类与字符选项
1.字符类
符号 | 描述 |
\\d | 可以匹配任意数字 |
\\s | 可以匹配任意空白字符,如空格,制表符和换行符 |
[abc] | 可以匹配a、b或c |
[^abc] | 可以匹配除a、b、c外的任意字符 |
2.使用字符选项创建多个可选的模式
3.7 重复
正则表达式的另一项强大功能是,其可以控制一个模式能够匹配多少次。
• ?: 0 次或 1 次。
• +: 1 次或多次。
• *: 0 次或多次。
x <- "1888 is the longest year in Roman numerals: MDCCCLXXXVIII"
str_view(x, "CC?")
str_view(x, "CC+")
str_view(x, 'C[LX]+')
• {n}:匹配 n 次。
• {n,}:匹配 n 次或更多次。
• {,m}:最多匹配 m 次。
• {n, m}:匹配 n 到 m 次。
3.9分组与回溯引用
括号可以用于消除复杂表达式中的歧义。
括号还可以定义“分组”
可以通过回溯引用(如\1、\2等)来引用这些分组。例如,以下的正则表达式可以找出名称中有重复的一对字母的所有水果:
ruit是个自带的向量,括号表示优先级,\1表示第一组,第一个反斜线表示转义
补充
去掉一个点
str_view(fruit, "(.)\\1", match = TRUE)
4.工具
• 确定与某种模式相匹配的字符串;
• 找出匹配的位置;
• 提取出匹配的内容;
• 使用新值替换匹配内容;
• 基于匹配拆分字符串。
4.1匹配检测
str_detect() 只返回是否符合的逻辑值,实际上计数更实用。
x <- c("apple", "banana", "pear")
str_detect(x, "e")
#> [1] TRUE FALSE TRUE
记住,从数学意义上来说,逻辑向量中的FALSE为0,TRUE为1。这使得在匹配特别大的向量时,sum()和mean()函数能够发挥更大的作用:
str_detect()和sum、mean连用,统计匹配的个数和比例。
# 有多少个以t开头的常用单词?
sum(str_detect(words, "^t"))#sum是计数
#> [1] 65
# 以元音字母结尾的常用单词的比例是多少?
mean(str_detect(words, "[aeiou]$"))#mean是比例。[]表示任选其一
#> [1] 0.277
复杂的正则表达式可拆分为几个简单的
找出不包含元音字母的所有单词:
# 找出至少包含一个元音字母的所有单词,然后取反
no_vowels_1 <- !str_detect(words, "[aeiou]")
# 找出仅包含辅音字母(非元音字母)的所有单词
no_vowels_2 <- str_detect(words, "^[^aeiou]+$")
identical(no_vowels_1, no_vowels_2)
匹配取子集操作
words[str_detect(words, "x$")]#方法一,逻辑取子集
#> [1] "box" "sex" "six" "tax"
str_subset(words, "x$")#方法二,str_subset() 直接用取子集函数
#> [1] "box" "sex" "six" "tax"
结合filter,对数据框里的行进行匹配取子集
df <- tibble(
word = words,
i = seq_along(word)
)
#发现用seq_along这个操作生成了行号。
df %>%
filter(str_detect(words, "x$"))
str_count计数:每个字符串各匹配几次
x <- c("apple", "banana", "pear")
str_count(x, "a")
#> [1] 1 3 1
# 平均来看,每个单词中有多少个元音字母?
mean(str_count(words, "[aeiou]"))
#> [1] 1.99
str_count和mutate连用,将匹配个数添加到表格新列
df %>%
mutate(
vowels = str_count(word, "[aeiou]"),
consonants = str_count(word, "[^aeiou]")
)
#偷偷查了一下这俩单词,元音和辅音
规律:str_view一个字符串只匹配一次,str_view_all匹配多次,但二者都不匹配重叠
4.3提取匹配内容
str_subset 提取匹配到的整个字符串
首先明确sentence是一个stringr自带向量,由字符串(句子)组成。里面星星点点带有几个颜色单词,
示例是从这个向量字符串中提取出颜色单词
length(sentences)
head(sentences)
class(sentences)
#>[1] "character"
#构建匹配模式,多种颜色任选,用|连接
#创建一个颜色名称向量
colors <- c(
"red", "orange", "yellow", "green", "blue", "purple"
)
color_match <- str_c(colors, collapse = "|")
color_match
#[1] "red|orange|yellow|green|blue|purple"
#用str_subset匹配取子集
has_color <- str_subset(sentences, color_match)
has_color
#取出匹配到多次的字符串
more <- sentences[str_count(sentences, color_match) > 1]
str_view_all(more, color_match)#查看所有匹配
str_extrac以列表的形式返回每个字符串的匹配
#提取每个字符串第一个匹配
str_extract(more, color_match)
#> [1] "blue" "green" "orange"
#使用str_extract_all() 函数,它会返回一个列表:
#提取每个字符串所有匹配(列表形式)
str_extract_all(more, color_match)
[[1]]
[1] "blue" "red"
[[2]]
[1] "green" "red"
[[3]]
[1] "orange" "red"
#simplify = TRUE,设置匹配同等长度
str_extract_all(more, color_match, simplify = TRUE)
#> [,1] [,2]
#> [1,] "blue" "red"
#> [2,] "green" "red"
#> [3,] "orange" "red"
x <- c("a", "a b", "a b c")
str_extract_all(x, "[a-z]", simplify = TRUE)
#> [,1] [,2] [,3]
#> [1,] "a" "" ""
#> [2,] "a" "b" ""
#> [3,] "a" "b" "c"
4.5分组匹配
举例来说,假设我们想从句子中提取出名词。我们先进行一种启发式实验,找出跟在a或the后面的所有单词。因为使用正则表达式定义“单词”有一点难度,所以我们使用一种简单的近似定义——至少有1个非空格字符的字符序列:
#定义匹配模式
#括号提高优先级别
#a 或者 the
#^ 以什么开头
#+ 匹配1次或者多次
noun <- "(a|the) ([^ ]+)"
#|是或,[]表示任意,^表示非,+表示1次或多次
has_noun <- sentences %>%
str_subset(noun) %>%#取子集
head(10)#取前十行
has_noun %>%
str_extract(noun)#提取每行第一个匹配,得到10个匹配结果
#> [1] "the smooth" "the sheet" "the depth" "a chicken"
#> [5] "the parked" "the sun" "the huge" "the ball"
#> [9] "the woman" "a helps"
str_extract() 函数可以给出完整匹配; str_match() 函数则可以给出每个独立分组。 str_
match() 返回的不是字符向量,而是一个矩阵,其中一列是完整匹配,后面的列是每个分
组的匹配
has_noun %>%
str_match(noun)
[,1] [,2] [,3]
[1,] "the smooth" "the" "smooth"
[2,] "the sheet" "the" "sheet"
[3,] "the depth" "the" "depth"
[4,] "a chicken" "a" "chicken"
[5,] "the parked" "the" "parked"
[6,] "the sun" "the" "sun"
[7,] "the huge" "the" "huge"
[8,] "the ball" "the" "ball"
[9,] "the woman" "the" "woman"
[10,] "a helps" "a" "helps"
# 如果数据是保存在 tibble 中的,那么使用 tidyr::extract() 会更容易。这个函数的工作方式
#与 str_match() 函数类似,只是要求为每个分组提供一个名称,以作为新列放在 tibble 中
tibble(sentence = sentences) %>%
tidyr::extract(
sentence, c("article", "noun"), "(a|the) ([^ ]+)",
remove = FALSE
)
4.7 替换匹配内容
str_replace 只替换每个字符串匹配到的第一个
str_replace_all替换每个字符串匹配到的所有
向量结合str_replace_all()同时执行多个替换:
回溯引用分组:交换第二个单词和第三个单词的顺序:
本来是
\1 \2 \3
通过设置,顺序交换
\1 \3 \2
sentences %>%
str_replace("([^ ]+) ([^ ]+) ([^ ]+)", "\\1 \\3 \\2") %>%
head(5)
#用不交换的对比一下
sentences %>%
head(5)
4.9 str_split 拆分
str_split()将字符串拆分为多个片段
将句子拆分成单词
按单词之间的空格,拆分为一个个单词
sentences %>%
head(5) %>%
str_split(" ")
拆分单个字符串,返回一个列表
和返回列表的其他stringr函数一样,可以通过设置simplify=TRUE返回一个矩阵:
n设定拆分片段的最大数量:
用字母、行、句子和单词边界boundary()
关于边界的探索:
str_view_all(x, boundary("word"))#以单词为界
str_view_all(x, boundary("sentence"))#以句子为界
str_view_all(x, boundary("letter"))#报错
#Error in match.arg(type) :
# 'arg' should be one of “character”, “line_break”, “sentence”, “word”
str_view_all(x, boundary("character"))
str_view_all(x, boundary("line_break"))
4.11 定位匹配内容
str_locate() 和str_locate_all() 函数可以给出每个匹配的开始位置和结束位置。
5.其他类型的模式
regex()的参数
- ignore_case = TRUE 忽略大小写
- multiline = TRUE 可以使得^和$从每行的开头和末尾开始匹配,而不是从完整字符串的开头和末尾开始匹配:
- comments = TRUE 可加注释
- dotall = TRUE dotall = TRUE 可以使得. 匹配包括\n 在内的所有字符。
regex(),之外的3 种函数:
- fixed() 按照字符串的字节形式进行精确匹配,不需要转义,忽略正则表达式中所有特殊字符
- coll() 使用标准排序规则来比较字符串
- boundary() 边界
6.正则表达式其他应用
R基础包中有两个常用函数,它们也可以使用正则表达式。