R 编程/文本处理
此页面包含处理 R 中字符串所需的所有资料。即使您只需要执行一些简单的任务,有关正则表达式的部分可能对理解页面其余内容也很有用。
此页面可能对您有所帮助,可以:
- 进行统计文本分析。
- 从无格式的文本文件中收集数据。
- 处理字符变量。
在本页面中,我们将学习如何读取文本文件以及如何使用 R 函数处理字符。处理字符的函数有两种,分别是简单函数和正则表达式。许多函数是标准 R **base** 包的一部分。
help.search(keyword = "character", package = "base")
但是,对于所有用户来说,它们的名称和语法并不直观。Hadley Wickham 开发了 **stringr** 包,该包定义了具有类似行为的函数,但它们的名称更容易记住,语法也更系统化[1]。
- 关键字:文本挖掘、自然语言处理
- 请参阅 CRAN 上有关自然语言处理的任务视图[2]
- 另请参见以下包 **tm**、**tau**、**languageR**、**scrapeR**。
读取和写入文本文件
[edit | edit source]**R** 可以使用 readLines()
或 scan()
读取任何文本文件。可以使用 readLines()
指定导入文本文件的编码。文本文件的整个内容可以读取到 R 对象中(例如,字符向量)。scan()
更加灵活。可以在第二个参数中指定预期的数据类型(例如,字符(0) 用于字符串)。
text <- readLines("file.txt",encoding="UTF-8")
scan("file.txt", character(0)) # separate each word
scan("file.txt", character(0), quote = NULL) # get rid of quotes
scan("file.txt", character(0), sep = ".") # separate each sentence
scan("file.txt", character(0), sep = "\n") # separate each line
我们可以使用 cat()
或 writeLines()
将 R 对象的内容写入文本文件。默认情况下,cat()
在写入文本文件时会连接向量。您可以通过添加选项 sep="\n"
或 fill=TRUE
来更改它。默认编码取决于您的计算机。
cat(text,file="file.txt",sep="\n")
writeLines(text, con = "file.txt", sep = "\n", useBytes = FALSE)
在读取文本文件之前,您可以查看其属性。nlines()
(**parser** 包)和 countLines()
(**R.utils** 包)计算文件中的行数。count.chars()
(**parser** 包)计算文件中每行的字节数和字符数。您也可以使用 file.show()
显示文本文件。
字符编码
[edit | edit source]R 提供了处理各种编码方案集的函数。如果您处理使用其他操作系统创建的文本文件,尤其是在语言不是英语并且包含许多重音和特定字符的情况下,这很有用。例如,Linux 中的标准编码方案是“UTF-8”,而 Windows 中的标准编码方案是“Latin1”。Encoding()
函数返回字符串的编码。iconv()
与 unix 命令 iconv 相似,并转换编码。
iconvlist()
提供计算机上可用的编码方案列表。readLines()
、scan()
和file.show()
也具有编码选项。is.utf8()
(**tau**)测试编码是否为“utf8”。is.locale()
(**tau**)测试编码是否与计算机上的默认编码相同。translate()
(**tau**)将编码转换为当前区域设置。fromUTF8()
(**descr**)的通用性低于iconv()
。utf8ToInt()
(**base**)
示例
[edit | edit source]以下示例在 Windows 下运行。因此,默认编码为“latin1”。
> texte <- "Hé hé"
> Encoding(texte)
[1] "latin1"
> texte2 <- iconv(texte,"latin1","UTF-8")
> Encoding(texte2)
[1] "UTF-8"
正则表达式
[edit | edit source]正则表达式是字符串集中的一种特定模式。例如,可以有以下模式:2 位数字、2 个字母和 4 位数字。**R** 提供了处理正则表达式的强大函数。**R** 中使用两种类型的正则表达式[3]
- 扩展正则表达式,由
‘perl = FALSE’
(默认值)使用, - Perl 风格的正则表达式,由
‘perl = TRUE’
使用。
还有一个名为 ‘fixed = TRUE’
的选项,可以将其视为字面正则表达式。fixed()
(**stringr**)等效于标准正则表达式函数中的 fixed=TRUE
。默认情况下,这些函数区分大小写。可以通过指定选项 ignore.case = TRUE
来更改此设置。
如果您不是正则表达式专家,那么您可能会发现 glob2rx()
很有用。此函数会为特定(“glob”或“通配符”)模式提供一些正则表达式建议
> glob2rx("abc.*")
[1] "^abc\\."
R 中使用正则表达式的函数
[edit | edit source]sub()
、gsub()
、str_replace()
(**stringr**)对字符串进行一些替换。grep()
、str_extract()
(**stringr**)提取一些值grepl()
、str_detect()
(**stringr**)检测模式是否存在。- 另请参见
splitByPattern()
(**R.utils**) - 另请参见 **gsubfn** 包中的
gsubfn()
。
扩展正则表达式(默认值)
[edit | edit source]"."
代表任何字符。"[ABC]"
表示 A、B 或 C。"[A-Z]"
表示 A 到 Z 之间的任何大写字母。"[0-9]"
表示 0 到 9 之间的任何数字。
以下是元字符 ‘$ * + . ? [ ] ^ { } | ( ) \’
的列表。如果您需要使用其中一个字符,请在其前面加上双反斜杠。
以下是正则表达式的一些类别:用于数字
‘[:digit:]’
数字:‘0 1 2 3 4 5 6 7 8 9’
。
用于字母
‘[:alpha:]’
字母字符:‘[:lower:]’
和‘[:upper:]’
。‘[:upper:]’
大写字母。‘[:lower:]’
小写字母。
请注意,字母字符集包含诸如 é è ê
之类的重音,这些重音在法语等一些语言中非常常见。因此,它比 "[A-Za-z]"
更通用,后者不包含带重音的字母。
用于其他字符
‘[:punct:]’
标点符号:‘! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~’
。‘[:space:]’
空格字符:制表符、换行符、垂直制表符、换页符、回车符和空格。‘[:blank:]’
空白字符:空格和制表符。‘[:cntrl:]’
控制字符。
其他类别的组合
[:alnum:]
字母数字字符:‘[:alpha:]’
和‘[:digit:]’
。‘[:graph:]’
图形字符:‘[:alnum:]’
和‘[:punct:]’
。‘[:print:]’
可打印字符:‘[:alnum:]’
、‘[:punct:]’
和空格。‘[:xdigit:]’
十六进制数字:‘0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f’
。
你可以在正则表达式后添加以下字符来量化重复次数
‘?’
前一项是可选的,最多匹配一次。‘*’
前一项将被匹配零次或多次。‘+’
前一项将被匹配一次或多次。‘{n}’
前一项被精确匹配 'n' 次。‘{n,}’
前一项被匹配 'n' 次或更多次。‘{n,m}’
前一项至少被匹配 'n' 次,但不超过 'm' 次。
^
强制正则表达式位于字符串的开头$
强制正则表达式位于字符串的结尾
如果你想了解更多,请查看以下两个帮助文件
>?regexp # gives some general explanations
>?grep # help file for grep(),regexpr(),sub(),gsub(),etc
本节为残缺内容。 你可以通过 扩展 来帮助 Wikibooks。 |
也可以使用“Perl 风格”的正则表达式。你只需要使用 perl=TRUE
选项。
如果你想删除字符串中的空格字符,可以使用 \\s
Perl 宏。
sub('\\s', '',x, perl = TRUE)
paste()
连接字符串。str_c()
(stringr) 执行类似的任务。cat()
打印和连接字符串。
> paste("toto","tata",sep=' ')
[1] "toto tata"
> paste("toto","tata",sep=",")
[1] "toto,tata"
> str_c("toto","tata",sep=",")
[1] "toto,tata"
> x <- c("a","b","c")
> paste(x,collapse=" ")
[1] "a b c"
> str_c(x, collapse = " ")
[1] "a b c"
> cat(c("a","b","c"), sep = "+")
a+b+c
strsplit()
: 根据字符向量 'x' 中与子字符串 'split' 的匹配结果,将 'x' 的元素拆分为子字符串。- 另见
str_split()
(stringr)。
> unlist(strsplit("a.b.c", "\\."))
[1] "a" "b" "c"
tokenize()
(tau) 将字符串拆分为标记。
> tokenize("abc defghk")
[1] "abc" " " "defghk"
nchar()
给出字符串的长度。注意,对于非 ASCII 编码,存在多种测量长度的方法。- 另见
str_length()
(stringr)
> nchar("abcdef")
[1] 6
> nchar(NA)
[1] NA
> nchar("René")
[1] 4
> nchar("René", type = "bytes")
[1] 5
grepl()
返回一个逻辑表达式 (TRUE 或 FALSE)。str_detect()
(stringr) 执行类似的任务。
> string <- "23 mai 2000"
> string2 <- "1 mai 2000"
> regexp <- "([[:digit:]]{2}) ([[:alpha:]]+) ([[:digit:]]{4})"
> grepl(pattern = regexp, x = string)
[1] TRUE
> str_detect(string, regexp)
[1] TRUE
> grepl(pattern = regexp, x = string2)
[1] FALSE
第一个为真,第二个为假,因为第一个数字中只有一个数字。
textcnt()
(tau) 统计文本中每个模式或每个词出现的次数。
> string <- "blabla 23 mai 2000 blabla 18 mai 2004"
> textcnt(string,n=1L,method="string")
blabla mai
2 2
attr(,"class")
[1] "textcnt"
cpos()
(cwhmisc) 返回子字符串在字符串中的位置。substring.location()
(cwhmisc) 执行相同的任务,但返回第一个和最后一个位置。
> cpos("abcdefghijklmnopqrstuvwxyz","p",start=1)
[1] 16
> substring.location("abcdefghijklmnopqrstuvwxyz","def")
$first
[1] 4
$last
[1] 6
regexpr()
返回正则表达式的起始位置。str_locate()
(stringr) 执行相同的任务。gregexpr()
类似于regexpr()
,但返回每个匹配项的起始位置。str_locate_all()
(stringr) 执行相同的任务。
> regexp <- "([[:digit:]]{2}) ([[:alpha:]]+) ([[:digit:]]{4})"
> string <- "blabla 23 mai 2000 blabla 18 mai 2004"
> regexpr(pattern = regexp, text = string)
[1] 8
attr(,"match.length")
[1] 11
> gregexpr(pattern = regexp, text = string)
[[1]]
[1] 8 27
attr(,"match.length")
[1] 11 11
> str_locate(string,regexp)
start end
[1,] 8 18
> str_locate_all(string,regexp)
[[1]]
start end
[1,] 8 18
[2,] 27 37
substr()
获取子字符串。str_sub()
(stringr) 类似。
> substr("simple text",1,3)
[1] "sim"
> str_sub("simple text",1,3)
[1] "sim"
first.word()
Hmisc 包中字符串或表达式的第一个词
> first.word("abc def ghk")
[1] "abc"
- 如果
value=T
,则grep()
返回正则表达式的值,如果value=F
,则返回其位置。
> grep(pattern = regexp, x = string , value = T)
[1] "23 mai 2000"
> grep(pattern = regexp, x = string2 , value = T)
character(0)
> grep(pattern = regexp, x = string , value = F)
[1] 1
> grep(pattern = regexp, x = string2 , value = F)
integer(0)
str_extract()
、str_extract_all()
、str_match()
、str_match_all()
(stringr) 和m()
(caroline 包) 类似于grep()
。str_extract()
和str_extract_all()
返回一个向量。str_match()
和str_match_all()
返回一个矩阵,而m()
返回一个数据框。
> library("stringr")
> regexp <- "([[:digit:]]{2}) ([[:alpha:]]+) ([[:digit:]]{4})"
> string <- "blabla 23 mai 2000 blabla 18 mai 2004"
> str_extract(string,regexp)
[1] "23 mai 2000"
> str_extract_all(string,regexp)
[[1]]
[1] "23 mai 2000" "18 mai 2004"
> str_match(string,regexp)
[,1] [,2] [,3] [,4]
[1,] "23 mai 2000" "23" "mai" "2000"
> str_match_all(string,regexp)
[[1]]
[,1] [,2] [,3] [,4]
[1,] "23 mai 2000" "23" "mai" "2000"
[2,] "18 mai 2004" "18" "mai" "2004"
> library("caroline")
> m(pattern = regexp, vect = string, names = c("day","month","year"), types = rep("character",3))
day month year
1 18 mai 2004
- 命名捕获正则表达式可用于在正则表达式中定义列名(这也用于记录正则表达式)。通过
devtools::install_github("tdhock/namedCapture")
安装 namedCapture 包以使用str_match_all_named()
。它使用基本函数gregexpr(perl=TRUE)
来解析 Perl 兼容正则表达式,并返回一个包含带列名的匹配矩阵的列表
> named.regexp <- paste0(
+ "(?<day>[[:digit:]]{2})",
+ " ",
+ "(?<month>[[:alpha:]]+)",
+ " ",
+ "(?<year>[[:digit:]]{4})")
> namedCapture::str_match_all_named(string, named.regexp)
[[1]]
day month year
[1,] "23" "mai" "2000"
[2,] "18" "mai" "2004"
sub()
进行替换。gsub()
类似于sub()
,但它替换模式的所有出现,而sub()
只替换第一次出现。str_replace()
(stringr) 类似于 sub,str_replace_all()
(stringr) 类似于 gsub。
在下面的例子中,我们有一个法语日期。正则表达式模式如下:2 位数字,一个空格,一些字母,一个空格,4 位数字。我们使用[[:digit:]]{2}
表达式捕获 2 位数字,使用[[:alpha:]]+
捕获字母,使用[[:digit:]]{4}
捕获 4 位数字。这三个子字符串中的每一个都被括号包围。第一个子字符串存储在"\\1"
中,第二个存储在"\\2"
中,第三个存储在"\\3"
中。
string <- "23 mai 2000"
regexp <- "([[:digit:]]{2}) ([[:alpha:]]+) ([[:digit:]]{4})"
sub(pattern = regexp, replacement = "\\1", x = string) # returns the first part of the regular expression
sub(pattern = regexp, replacement = "\\2", x = string) # returns the second part
sub(pattern = regexp, replacement = "\\3", x = string) # returns the third part
在下面的例子中,我们比较了sub()
和gsub()
的结果。第一个删除了第一个空格,而第二个删除了文本中的所有空格。
> text <- "abc def ghk"
> sub(pattern = " ", replacement = "", x = text)
[1] "abcdef ghk"
> gsub(pattern = " ", replacement = "", x = text)
[1] "abcdefghk"
chartr()
替换表达式中的字符。它代表“字符转换”。replacechar()
(cwhmisc) 执行相同的工作...- 以及
str_replace_all()
(stringr)。
> chartr(old="a",new="o",x="baba")
[1] "bobo"
> chartr(old="ab",new="ot",x="baba")
[1] "toto"
> replacechar("abc.def.ghi.jkl",".","_")
[1] "abc_def_ghi_jkl"
> str_replace_all("abc.def.ghi.jkl","\\.","_")
[1] "abc_def_ghi_jkl"
tolower()
将大写字母转换为小写。toupper()
将小写字母转换为大写。capitalize()
(Hmisc) 将字符串的第一个字母大写- 另请参阅 cwhmisc 包中的
cap()
、capitalize()
、lower()
、lowerize()
和CapLeading()
。
> tolower("ABCdef")
[1] "abcdef"
> toupper("ABCdef")
[1] "ABCDEF"
> capitalize("abcdef")
[1] "Abcdef"
padding()
(cwhmisc) 用某些字符填充字符串以适应给定的长度。另请参阅str_pad()
(stringr)。
> library("cwhmisc")
> padding("abc",10," ","center") # adds blanks such that the length of the string is 10.
[1] " abc "
> str_pad("abc",width=10,side="center", pad = "+")
[1] "+++abc++++"
> str_pad(c("1","11","111","1111"),3,side="left",pad="0")
[1] "001" "011" "111" "1111"
请注意,str_pad()
非常慢。例如,对于长度为 10,000 的向量,计算时间非常长。padding()
似乎无法处理字符向量,但最好的解决方案可能是将sapply()
和padding()
函数一起使用。
>library("stringr")
>library("cwhmisc")
>a <- rep(1,10^4)
> system.time(b <- str_pad(a,3,side="left",pad="0"))
utilisateur système écoulé
50.968 0.208 73.322
> system.time(c <- sapply(a, padding, space = 3, with = "0", to = "left"))
utilisateur système écoulé
7.700 0.020 12.206
trimws()
(memisc 包) 修剪前导空格和尾随空格。trim()
(gdata 包) 执行相同的工作。- 另请参阅
str_trim()
(stringr)
> library("memisc")
> trimws(" abc def ")
[1] "abc def"
> library("gdata")
> trim(" abc def ")
[1] "abc def"
> str_trim(" abd def ")
[1] "abd def"
==
如果两个字符串相同则返回 TRUE,否则返回 false。
> "abc"=="abc"
[1] TRUE
> "abc"=="abd"
[1] FALSE
很少有包实现了Levenshtein 距离,即两个字符串之间的距离。
- 基本包 utils 中的
adist()
- MiscPsycho 中的
stringMatch()
- stringdist 中的
stringdist()
- RecordLinkage 中的
levenshteinDist()
此处提供了比较levenshteinDist()
和stringdist()
速度的基准测试:[1]。
> adist("test","tester")
[1] 2
stringMatch()
(MiscPsycho) 计算如果normalize="YES"
,则 Levenshtein 距离将除以每个字符串的最大长度。
> library("MiscPsycho")
> stringMatch("test","tester",normalize="NO",penalty=1,case.sensitive = TRUE)
[1] 2
agrep()
使用Levenshtein 距离搜索近似匹配。
- 如果 'value = TRUE',则返回字符串的值
- 如果 'value = FALSE',则返回字符串的位置
- max返回最大 Levenshtein 距离。
> agrep(pattern = "laysy", x = c("1 lazy", "1", "1 LAZY"), max = 2, value = TRUE)
[1] "1 lazy"
> agrep("laysy", c("1 lazy", "1", "1 LAZY"), max = 3, value = TRUE)
[1] "1 lazy"
deparse()
:将未评估的表达式转换为字符字符串。char.expand()
(base) 根据目标扩展字符串。pmatch()
(base) 和charmatch()
(base) 在第二个参数中查找第一个参数元素的匹配项。
> pmatch(c("a","b","c","d"),table = c("b","c"), nomatch = 0)
[1] 0 1 2 0
make.unique()
使字符字符串唯一。如果要使用字符串作为数据中的标识符,这将很有用。
> make.unique(c("a", "a", "a"))
[1] "a" "a.1" "a.2"
- ↑ Hadley Wickham "stringr: modern, consistent string processing" The R Journal, December 2010, Vol 2/2, http://journal.r-project.org/archive/2010-2/RJournal_2010-2_Wickham.pdf
- ↑ https://cran.r-project.org.cn/web/views/NaturalLanguageProcessing.html
- ↑ 在以前版本(< 2.10)中,我们还在 R 中拥有基本正则表达式:
- 扩展正则表达式,由
extended = TRUE
(默认值)使用, - 基本正则表达式,由
extended = FALSE
(在 R 2.10 中已弃用)使用。
‘extended = FALSE’
) 现在已弃用,因此extended
选项在 2.11 版中已弃用。 - 扩展正则表达式,由