第 3 章 字符串

3.1 使用 grep、sub、gsub 进行搜索和替换

3.1.1 问题

你想要搜索或替换字符串中特定的文本。

3.1.2 方案

两个常用字符串搜索函数 grep()grepl()。两个常用字符串替换函数 sub()gsub()。它们都是向量化操作,会应用到输入字符向量的每一个元素中。

3.1.2.1 文本搜索

grep()grepl() 函数输入的第一个参数都是带有正则表达式的字符串或者固定的字符串(需要设定选项 fixed=TRUE ),它们的不同之处是前者返回匹配的索引或值向量,而后者返回一个逻辑向量。

下面通过简单的例子理解它们的用法和区别:从小写字母向量中搜索 c

grep("c", letters)
#> [1] 3
grepl("c", letters)
#>  [1] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
#>  [9] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [17] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
#> [25] FALSE FALSE

grep() 函数设定选项 value=TRUE 可以返回匹配的值。

grep("c", letters, value = TRUE)
grep("c", c("dog", "cat"), value = TRUE)

正则表达式提供了一种表达字符模式的强大方式(详情查看 ?regex),我们可以将它应用于文本的搜索中。例如,我们想搜索字符向量中有4个数字连续出现的字符:

grep("\\d{4}", c("This will not match", "2018-04-11, This will match"))
#> [1] 2

3.1.2.2 文本替换

大部分情况下我们不仅仅想搜索到文本,而且还想要在搜索的基础上进行替换,这可以通过 sub()gsub() 函数实现。这两个函数参数是一样的,第一个参数是搜索模式,第二个参数是替换模式,第三个参数是要操作的字符向量。两个函数的区别是前者只会替换字符串中第一个匹配的模式,而 gsub()gglobal 的缩写)会替换字符串中所有匹配模式。

例如,我们想要将字符向量中的年份全部替换为 2019。

sub(pattern = "\\d{4}", replacement = "2019", x = c("This will not match", 
  "2018-04-11, 2017-04-11", "2018-04-12"))
#> [1] "This will not match"    "2019-04-11, 2017-04-11"
#> [3] "2019-04-12"

gsub(pattern = "\\d{4}", replacement = "2019", x = c("This will not match", 
  "2018-04-11, 2017-04-11", "2018-04-12"))
#> [1] "This will not match"    "2019-04-11, 2019-04-11"
#> [3] "2019-04-12"

要操作的对象第二个元素包含2个可以匹配的模式,使用 sub() 只会将第一个替换为 2019,而使用 gsub() 会将所有能够匹配的模式都替换为 2019。

3.2 通过变量创建字符串

3.2.1 问题

你想要通过变量创建一个字符串。

3.2.2 方案

两种从变量创建字符串的通用方法是使用 paste()sprintf() 函数。对向量来说,paste 更加有用;sprintf 则常用于对输出实现精确的控制。

3.2.2.1 使用 paste()

a <- "apple"
b <- "banana"

# 将a、b变量内容连到一起,并用空格隔开
paste(a, b)
#> [1] "apple banana"

# 如果不想要空格,可以设定参数 sep='', 或使用函数
# paste0():
paste(a, b, sep = "")
#> [1] "applebanana"
paste0(a, b)
#> [1] "applebanana"

# 用逗号加空格分开:
paste(a, b, sep = ", ")
#> [1] "apple, banana"

# 设定一个字符向量
d <- c("fig", "grapefruit", "honeydew")

# 如果输入是一个向量,输出会将其每个元素堆叠到一起:
paste(d, collapse = ", ")
#> [1] "fig, grapefruit, honeydew"

# 如果输入是一个标量和一个向量,
# 结果会将标量与向量里每个元素放到一起
# 并返回一个向量(译者注:R向量化操作的循环对齐原则):
paste(a, d)
#> [1] "apple fig"        "apple grapefruit"
#> [3] "apple honeydew"

# 使用 sep 和 collapse 参数:
paste(a, d, sep = "-", collapse = ", ")
#> [1] "apple-fig, apple-grapefruit, apple-honeydew"

3.2.2.2 使用 sprintf()

另一种方式是使用 sprintf 函数,它来自于 C 语言。

想要在字符串或字符变量中进行取代操作,使用 %s

a <- "string"
sprintf("This is where a %s goes.", a)
#> [1] "This is where a string goes."

如果是整数,可以使用 %d 或它的变体:

x <- 8
sprintf("Regular:%d", x)
#> [1] "Regular:8"

# 可以以空格开头,替代一定数量字符。
sprintf("Leading spaces:%4d", x)
#> [1] "Leading spaces:   8"

# 也可以使用 0 替代一定数量字符。
sprintf("Leading zeros:%04d", x)
#> [1] "Leading zeros:0008"

对浮点数而言,使用 %f 进行标准释义,而 %e 或者 %E 则代表指数。你也可以使用 %g 或者 %G 让程序自动根据有效位数进行两种格式的转换。下面是 R help 页面中关于 sprintf 的例子:

sprintf("%f", pi)         # "3.141593"
sprintf("%.3f", pi)       # "3.142"
sprintf("%1.0f", pi)      # "3"
sprintf("%5.1f", pi)      # "  3.1"
sprintf("%05.1f", pi)     # "003.1"
sprintf("%+f", pi)        # "+3.141593"
sprintf("% f", pi)        # " 3.141593"
sprintf("%-10f", pi)      # "3.141593  "   (左对齐)
sprintf("%e", pi)         # "3.141593e+00"
sprintf("%E", pi)         # "3.141593E+00"
sprintf("%g", pi)         # "3.14159"
sprintf("%g",   1e6 * pi) # "3.14159e+06"  (指数化)
sprintf("%.9g", 1e6 * pi) # "3141592.65"   (固定)
sprintf("%G", 1e-6 * pi)  # "3.14159E-06"

%m.nf 格式规范中:m 代表域宽,它是输出字符串中字符的最小位数,可以以空格或 0 开头。n 代表精度,它指小数点后的数字位数。

其他混合操作:

x <- "string"
sprintf("Substitute in multiple strings: %s %s", x, "string2")
#> [1] "Substitute in multiple strings: string string2"

# 想要打印百分号,可以使用 '%%'
sprintf("A single percent sign here %%")
#> [1] "A single percent sign here %"

3.2.2.3 注意

关于更多脚本输出的信息可以查看用 R 写入文本,分析输出到文件