第十章 使用stringr处理字符串

重点正则表达式——难!!!

提要

R语言使用逗号连接字符串 r语言 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")

R语言使用逗号连接字符串 r语言 stringr_bc_02


R语言使用逗号连接字符串 r语言 stringr_R语言使用逗号连接字符串_03


用"."————任意字符匹配(除了换行符):

str_view(x, ".a.")

R语言使用逗号连接字符串 r语言 stringr_字符串_04

如果需要匹配”.“需要用正则表达式转义:正则表达式  \. 的字符串形式应是   \\.

R语言使用逗号连接字符串 r语言 stringr_R语言使用逗号连接字符串_05


那么如何匹配 \ 这个字符呢?

这意味着要想匹配字符\,我们需要输入"\\\\"----需要4个反斜杠来匹配1个反斜杠!

3.3 锚点–设置锚点,以便R从字符串的开头或末尾进行匹配

R语言使用逗号连接字符串 r语言 stringr_正则表达式_06


R语言使用逗号连接字符串 r语言 stringr_字符串_07


记忆:始于权力(^),终于金钱($)


强制正则表达式匹配一个完整字符串。

R语言使用逗号连接字符串 r语言 stringr_bc_08

3.5 字符类与字符选项

1.字符类

符号

描述

\\d

可以匹配任意数字

\\s

可以匹配任意空白字符,如空格,制表符和换行符

[abc]

可以匹配a、b或c

[^abc]

可以匹配除a、b、c外的任意字符

2.使用字符选项创建多个可选的模式

R语言使用逗号连接字符串 r语言 stringr_正则表达式_09

3.7 重复

正则表达式的另一项强大功能是,其可以控制一个模式能够匹配多少次。

• ?: 0 次或 1 次。
• +: 1 次或多次。
• *: 0 次或多次。
x <- "1888 is the longest year in Roman numerals: MDCCCLXXXVIII"
str_view(x, "CC?")

R语言使用逗号连接字符串 r语言 stringr_字符串_10

str_view(x, "CC+")
str_view(x, 'C[LX]+')

R语言使用逗号连接字符串 r语言 stringr_R语言使用逗号连接字符串_11

• {n}:匹配 n 次。
• {n,}:匹配 n 次或更多次。
• {,m}:最多匹配 m 次。
• {n, m}:匹配 n 到 m 次。

R语言使用逗号连接字符串 r语言 stringr_正则表达式_12

3.9分组与回溯引用

括号可以用于消除复杂表达式中的歧义。

括号还可以定义“分组”

可以通过回溯引用(如\1、\2等)来引用这些分组。例如,以下的正则表达式可以找出名称中有重复的一对字母的所有水果:

R语言使用逗号连接字符串 r语言 stringr_R语言使用逗号连接字符串_13


ruit是个自带的向量,括号表示优先级,\1表示第一组,第一个反斜线表示转义

补充

去掉一个点

str_view(fruit, "(.)\\1", match = TRUE)

R语言使用逗号连接字符串 r语言 stringr_正则表达式_14

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匹配多次,但二者都不匹配重叠

R语言使用逗号连接字符串 r语言 stringr_bc_15

4.3提取匹配内容
str_subset 提取匹配到的整个字符串

首先明确sentence是一个stringr自带向量,由字符串(句子)组成。里面星星点点带有几个颜色单词,

示例是从这个向量字符串中提取出颜色单词

R语言使用逗号连接字符串 r语言 stringr_R语言使用逗号连接字符串_16

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替换每个字符串匹配到的所有

R语言使用逗号连接字符串 r语言 stringr_字符串_17


向量结合str_replace_all()同时执行多个替换:

R语言使用逗号连接字符串 r语言 stringr_字符串_18


回溯引用分组:交换第二个单词和第三个单词的顺序:

本来是

\1 \2 \3

通过设置,顺序交换

\1 \3 \2

sentences %>%
str_replace("([^ ]+) ([^ ]+) ([^ ]+)", "\\1 \\3 \\2") %>%
head(5)
#用不交换的对比一下
sentences %>%
head(5)

R语言使用逗号连接字符串 r语言 stringr_bc_19

4.9 str_split 拆分

str_split()将字符串拆分为多个片段
将句子拆分成单词
按单词之间的空格,拆分为一个个单词

sentences %>%
head(5) %>%
str_split(" ")

R语言使用逗号连接字符串 r语言 stringr_正则表达式_20


R语言使用逗号连接字符串 r语言 stringr_字符串_21


拆分单个字符串,返回一个列表

和返回列表的其他stringr函数一样,可以通过设置simplify=TRUE返回一个矩阵:

n设定拆分片段的最大数量:

R语言使用逗号连接字符串 r语言 stringr_正则表达式_22


R语言使用逗号连接字符串 r语言 stringr_R语言使用逗号连接字符串_23


用字母、行、句子和单词边界boundary()

关于边界的探索:

str_view_all(x, boundary("word"))#以单词为界

R语言使用逗号连接字符串 r语言 stringr_R语言使用逗号连接字符串_24

str_view_all(x, boundary("sentence"))#以句子为界

R语言使用逗号连接字符串 r语言 stringr_字符串_25

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() 函数可以给出每个匹配的开始位置和结束位置。

R语言使用逗号连接字符串 r语言 stringr_正则表达式_26

5.其他类型的模式

R语言使用逗号连接字符串 r语言 stringr_R语言使用逗号连接字符串_27


regex()的参数

  • ignore_case = TRUE 忽略大小写
  • multiline = TRUE 可以使得^和$从每行的开头和末尾开始匹配,而不是从完整字符串的开头和末尾开始匹配:
  • comments = TRUE 可加注释
  • dotall = TRUE dotall = TRUE 可以使得. 匹配包括\n 在内的所有字符。

regex(),之外的3 种函数:

  • fixed() 按照字符串的字节形式进行精确匹配,不需要转义,忽略正则表达式中所有特殊字符
  • coll() 使用标准排序规则来比较字符串
  • boundary() 边界

6.正则表达式其他应用

R基础包中有两个常用函数,它们也可以使用正则表达式。

R语言使用逗号连接字符串 r语言 stringr_R语言使用逗号连接字符串_28


R语言使用逗号连接字符串 r语言 stringr_正则表达式_29


R语言使用逗号连接字符串 r语言 stringr_bc_30


R语言使用逗号连接字符串 r语言 stringr_正则表达式_31