7.2 字符串
文本数据存储在字符向量中(或字符数组中,虽然这较少见)。重要的是,字符向量中的每个元素都是字符串,而非单独的字符。在R中,“字符串”是个常用的非正式术语,因为正式的“字符向量元素”读起来相当拗口。
文本的基本单位是字符向量,这意味着大部分字符串处理函数也能用于字符串向量,这与数学运算的向量化方式相同。
7.2.1 创建和打印字符串
如你所见,字符向量可用c
函数创建。我们可以用单引号或双引号把字符串引用起来,只要引号之间匹配即可。不过,使用双引号更为标准:
c(
"You should use double quotes most of the time",
'Single quotes are better for including " inside the string'
)
## [1] "You should use double quotes most of the time"
## [2] "Single quotes are better for including \" inside the string"
paste
函数能将不同字符串组合在起来。在它传入的参数向量中,每个元素都能自我循环以达到最长的矢量长度,然后字符串就被拼接在一起,中间以空格分开。可以使用参数sep
更改分隔符,或使用相关的paste0
函数去掉分隔符。所有的字符串被组合后,可使用collapse
参数把结果收缩成一个包含所有元素的字符串:
paste(c("red", "yellow"), "lorry")
## [1] "red lorry" "yellow lorry"
paste(c("red", "yellow"), "lorry", sep = "-")
## [1] "red-lorry" "yellow-lorry"
paste(c("red", "yellow"), "lorry", collapse = ", ")
## [1] "red lorry, yellow lorry"
paste0(c("red", "yellow"), "lorry")
## [1] "redlorry" "yellowlorry"
toString
函数是paste
的变种,它在打印向量时非常有用。它使用逗号和空格分隔每个元素,且可限制打印的数量。在下例中,width = 40
将输出限制为40个字符:
x <- (1:15) ^ 2
toString(x)
## [1] "1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225"
toString(x, width = 40)
## [1] "1, 4, 9, 16, 25, 36, 49, 64, 81, 100...."
cat
是一个低级函数,工作原理类似于paste
,不过格式功能更少。你将很少直接调用它。不过,你应该对它有所了解,因为它是大部分print
函数的基础。cat
也可接受一个file
参数1,将输出写入文件:
1 再学究气点,它接受一个由file
函数返回的文件的路径或连接。
cat(c("red", "yellow"), "lorry")
## red yellow lorry
通常情况下,当字符串打印到控制台时,它们会以双引号括起来。如果对它们使用noquote
函数,就可以去掉这些引号。有时,这会使文本更具可读性:
x <- c(
"I", "saw", "a", "saw", "that", "could", "out",
"saw", "any", "other", "saw", "I", "ever", "saw"
)
y <- noquote(x)
x
## [1] "I" "saw" "a" "saw" "that" "could" "out" "saw"
## [9] "any" "other" "saw" "I" "ever" "saw"
y
## [1] I saw a saw that could out saw any other saw
## [12] I ever saw
7.2.2 格式化数字
有几个函数可用于数字的格式化。formatC
可让你使用C语言的格式化风格来指定使用固定型或科学型的格式、小数的位数以及输出的宽度。无论使用哪种选项,输入都应该是numeric
类型(包括数组),且输出是character
字符向量或数组:
pow <- 1:3
(powers_of_e <- exp(pow))
## [1] 2.718 7.389 20.086
formatC(powers_of_e)
## [1] "2.718" "7.389" "20.09"
formatC(powers_of_e, digits = 3) #指定三个数字
## [1] "2.72" "7.39" "20.1"
formatC(powers_of_e, digits = 3, width = 10) #前面加上一个空格
## [1] " 2.72" " 7.39" " 20.1"
formatC(powers_of_e, digits = 3, format = "e") #科学格式
## [1] "2.718e+00" "7.389e+00" "2.009e+01"
formatC(powers_of_e, digits = 3, flag = "+") #前面加上+
## [1] "+2.72" "+7.39" "+20.1"
R还提供了更通用的C风格的格式化函数sprintf
。与sprintf
在其他任何语言中的工作方式一样:第一个参数包含字符串或数字变量的占位符,其他参数则将逐个代入这些占位符。不过请记住,R中大部分的数值是浮点值而非整数。
sprint
的第一个参数指定了一个格式化字符串,其中包括其他值的占位符。例如:%s
代表另一个字符串,%f
和%e
分别代表固定型格式和科学型格式的浮点数,%d
表示整数。其他参数的值将替换占位符。与paste
函数类似,较短长度的输入将循环自身以匹配最长的输入:
sprintf("%s %d = %f", "Euler's constant to the power", pow, powers_of_e)
## [1] "Euler's constant to the power 1 = 2.718282"
## [2] "Euler's constant to the power 2 = 7.389056"
## [3] "Euler's constant to the power 3 = 20.085537"
sprintf("To three decimal places, e ^ %d = %.3f", pow, powers_of_e)
## [1] "To three decimal places, e ^ 1 = 2.718"
## [2] "To three decimal places, e ^ 2 = 7.389"
## [3] "To three decimal places, e ^ 3 = 20.086"
sprintf("In scientific notation, e ^ %d = %e", pow, powers_of_e)
## [1] "In scientific notation, e ^ 1 = 2.718282e+00"
## [2] "In scientific notation, e ^ 2 = 7.389056e+00"
## [3] "In scientific notation, e ^ 3 = 2.008554e+01"
其他格式化数字的方法有format
和prettyNum
这两个函数。format
提供的格式化字符串的语法稍有不同,与formatC
的用法基本类似。而prettyNum
则非常适合于格式化那些非常大或非常小的数字:
format(powers_of_e)
## [1] " 2.718" " 7.389" "20.086"
format(powers_of_e, digits = 3) #至少三个数字
## [1] " 2.72" " 7.39" "20.09"
format(powers_of_e, digits = 3, trim = TRUE) #去掉多余的0
## [1] "2.72" "7.39" "20.09"
format(powers_of_e, digits = 3, scientific = TRUE)
## [1] "2.72e+00" "7.39e+00" "2.01e+01"
prettyNum(
c(1e10, 1e-20),
big.mark = ",",
small.mark = " ",
preserve.width = "individual",
scientific = FALSE
)
## [1] "10,000,000,000" "0.00000 00000 00000 00001"
7.2.3 特殊字符
有一些特殊的字符可以被包含在字符串中。例如,我们可以通过\t
插入一个制表符。在下例中,我们使用cat
而非print
,因为print
执行的额外的转换动作会把制表符\t
转换成反斜杠和一个“t.”。cat
的参数fill = TRUE
使光标在一行结束后移动到下一行:
cat("foo\tbar", fill = TRUE)
## foo bar
将光标移动到下一行是通过打印换行符\n
完成的(这在所有平台上都一样。在R中,不要使用\r
或\r\n
来打印换行符,因为\r
会将光标移动到当前行的开始并覆盖你所写的内容):
cat("foo\nbar", fill = TRUE)
## foo
## bar
在R的内部代码中,空字符\0
用于终止字符串。然而,显式地把它们包含在字符串中是错误的(旧版本R会丢弃字符串中空字符之后的内容):
cat("foo\0bar", fill = TRUE) #这会抛出一个错误
打印反斜杠符时需要连续输入两个反斜杠符,以免被误认为特殊字符。在下例中,输入两个反斜杠,打印时只会看到一个:
cat("foo\\bar", fill = TRUE)
## foo\bar
如果我们需要在字符串中使用双引号,那么双引号符前必须加一个反斜杠来转义。同样地,如果要在字符串中使用单引号,则单引号需要被转义:
cat("foo\"bar", fill = TRUE)
## foo"bar
cat('foo\'bar', fill = TRUE)
## foo'bar
与之相反,如果在被双引号引用的字符串中使用单引号,或在被单引号引用的字符串中使用双引号,则并不需要对其进行转义:
cat("foo'bar", fill = TRUE)
## foo'bar
cat('foo"bar', fill = TRUE)
## foo"bar
通过打印报警符\a
能让我们的电脑发出提示声(beep),不过alarm
函数也能完成此功能且可读性更好。当想要程序在一个耗时很长的分析任务结束后主动通知你(你不在开放式的办公室),这个函数就能派上用场:
cat("\a")
alarm()
7.2.4 更改大小写
使用toupper
和tolower
函数能把字符串中的字符全部转换为大写或小写:
toupper("I'm Shouting")
## [1] "I'M SHOUTING"
tolower("I'm Whispering")
## [1] "i'm whispering"
7.2.5 截取字符串
有两个函数可用于从字符串中截取子串:substring
和substr
。在大多数情况下,你可以随便选一个使用。不过,如果你传入了不同长度的向量参数,它们的行为会略有不同。对substring
来说,输出的长度与最长的输入一样;而对substr
来说,输出的长度只与第一个输入的相等:
woodchuck <- c(
"How much wood would a woodchuck chuck",
"If a woodchuck could chuck wood?",
"He would chuck, he would, as much as he could",
"And chuck as much wood as a woodchuck would",
"If a woodchuck could chuck wood."
)
substring(woodchuck, 1:6, 10)
## [1] "How much w" "f a woodc" " would c" " chuck " " woodc"
## [6] "uch w"
substr(woodchuck, 1:6, 10)
## [1] "How much w" "f a woodc" " would c" " chuck " " woodc"
7.2.6 分割字符串
paste
及其相关函数能把字符串组合在一起。strsplit
则正好相反,它在指定的某些点上分割字符串。我们可以把上例中的土拨鼠绕口字符串按空格分开。在下例中,fixed = TRUE
意味着split
的参数是固定长度的字符串而非正则表达式:
strsplit(woodchuck, " ", fixed = TRUE)
## [[1]]
## [1] "How" "much" "wood" "would" "a" "woodchuck"
## [7] "chuck"
##
## [[2]]
## [1] "If" "a" "woodchuck" "could" "chuck" "wood?"
##
## [[3]]
## [1] "He" "would" "chuck," "he" "would," "as" "much"
## [8] "as" "he" "could"
##
## [[4]]
## [1] "And" "chuck" "as" "much" "wood" "as"
## [7] "a" "woodchuck" "would"
##
## [[5]]
## [1] "If" "a" "woodchuck" "could" "chuck" "wood."
请注意,strsplit
返回的是列表(而非字符向量或矩阵)。这是因为它的结果可能由不同长度的字符向量组成。当你只传入一个字符串时,这种情况很容易被忽视。请小心!
在我们的例子中,某些词最后的逗号有些烦人。最好的方法是在空格分割符后加一个可选的逗号,使用正则表达式就很容易搞定。?
意味着“前面的字符可选”:
strsplit(woodchuck, ",? ")
## [[1]]
## [1] "How" "much" "wood" "would" "a" "woodchuck"
## [7] "chuck"
##
## [[2]]
## [1] "If" "a" "woodchuck" "could" "chuck" "wood?"
##
## [[3]]
## [1] "He" "would" "chuck" "he" "would" "as" "much" "as"
## [9] "he" "could"
##
## [[4]]
## [1] "And" "chuck" "as" "much" "wood" "as"
## [7] "a" "woodchuck" "would"
##
## [[5]]
## [1] "If" "a" "woodchuck" "could" "chuck" "wood."
7.2.7 文件路径
R有一个工作目录,默认为文件被读写的地方。我们可以使用getwd
查看到它的位置,并使用setwd
来改变它:
getwd()
## [1] "d:/workspace/LearningR"
setwd("c:/windows")
getwd()
## [1] "c:/windows"
请注意,每个路径的目录部分由正斜杠分隔,即使在Windows下也是这样。为了保持可移植性,在R中你可以始终对路径使用正斜杠。根据操作系统的不同,文件处理函数能够魔术般地把它们自动替换为反斜杠。
你也可以使用双反斜杠来表示Windows的路径,不过正斜杠仍为首选:
"c:\\windows" #记住使用两个斜杠
"\\\\myserver\\mydir" #对于UNC的命名法,需在开始使用四个斜杠
或者,你可以使用file.path
来从各个目录中创建文件路径。它会自动地在目录名称之间插入正斜杠。它就像一个更加简单快速的为处理路径而定制的paste
函数:
file.path("c:", "Program Files", "R", "R-devel")
## [1] "c:/Program Files/R/R-devel"
R.home() #同样也是R的安装目录
## [1] "C:/PROGRA~1/R/R-devel"
路径可以是绝对路径(从驱动器名称或网络共享处开始)或相对目录(相对于当前工作目录)。在后一种情况中,.
用于当前目录而..
用于父目录。~
代表当前用户主目录。path.expand
能将相对路径转换为绝对路径:
path.expand(".")
## [1] "."
path.expand("..")
## [1] ".."
path.expand("~")
## [1] "C:\\Users\\richie\\Documents"
basename
只返回文件名,而不包括前面的目录位置。与之相反,dirname
只返回文件的目录:
file_name <- "C:/Program Files/R/R-devel/bin/x64/RGui.exe" basename(file_name)
## [1] "RGui.exe"
dirname(file_name)
## [1] "C:/Program Files/R/R-devel/bin/x64"