R中的普通文本处理-汇总

介绍:
1. 文本文件的读写2. 字符统计和字符翻译
3. 字符串连接
4. 字符串拆分
5. 字符串查询
6. 字符串替换
7. 字符串提取

说明:

  • 普通文本文件不同于我们平时接触到的表格式文本文件,这里的文本文件是纯文本文件,里面包含的大部分都是字符串;而表格式文本文件大多是行列比较整齐的数据文件,读取这一类的文件,需要用到read.table()或read.csv()之类的函数。
  • 关于正则表达式的介绍暂时不涉及
  • stringr、parser、R.utils等包暂时也不涉及,但不可否认它们提供的函数可用性更高些!

1.文本文件的读写

R里面读取文本文件的函数主要有readLines()和scan(),两者都可以指定读入内容的编码方式(通过encoding参数设置),整个文本读入R中之后会被存储在一个字符型的向量里。

text <- readLines("file.txt", encoding = "UTF-8")  #假设有这么一个文件可供使用
scan("file.txt", what = character(0))  #默认设置,每个单词作为字符向量的一个元素
scan("file.txt", what = character(0), sep = "\n")  #设置成每一行文本作为向量的一个元素,这类似于readLines
scan("file.txt", what = character(0), sep = ".")  #设置成每一句文本作为向量的一个元素

同样R对象里面的内容也可以写入文本文件中,主要有cat()和writeLines()。默认情况下,cat()会将向量里的元素连在一起写入到文件中去,但可以sep参数设置分割符。

cat(text, file = "file.txt", sep = "\n")
writeLines(text, con = "file.txt", sep = "\n", useBytes = F)

2.字符统计及字符翻译

nchar()用来统计每个元素的字符个数,注意与length()的区别,length()用来统计每个向量中的元素个数。

x <- c("we are the world", "we are the children")
x
## [1] "we are the world"    "we are the children"
nchar(x)
## [1] 16 19
length(x)
## [1] 2

nchar("")
## [1] 0
length("")  #虽然字符为空,但是它仍然是一个元素。
## [1] 1

字符翻译常用的函数有tolower(),toupper()和chartr()

dna <- "AgCTaaGGGcctTagct"
dna
## [1] "AgCTaaGGGcctTagct"
tolower(dna)
## [1] "agctaagggccttagct"
toupper(dna)
## [1] "AGCTAAGGGCCTTAGCT"
chartr("Tt", "Uu", dna)  #将T碱基替换成U碱基
## [1] "AgCUaaGGGccuUagcu"

3.字符串连接

paste()是R中用来连接字符串的函数,但是它的功能又远远不止于此。

paste("control", 1:3, sep = "_")
## [1] "control_1" "control_2" "control_3"

x <- list(a = "aa", b = "bb")
y <- list(c = 1, d = 2)
paste(x, y, sep = "-")
## [1] "aa-1" "bb-2"

paste(x, y, sep = "-", collapse = ";")
## [1] "aa-1;bb-2"
paste(x, collapse = ":")
## [1] "aa:bb"

x
## $a
## [1] "aa"
## 
## $b
## [1] "bb"
as.character(x)  #将其它类型的对象转换成字符
## [1] "aa" "bb"
unlist(x)
##    a    b 
## "aa" "bb"

4.字符串拆分

strsplit()是一个拆分函数,该函数可以使用正则表达式进行匹配拆分。其命令形式为:
strsplit(x, split, fixed= F, perl= F, useBytes= F)

  • 参数x为字符串格式向量,函数依次对向量的每个元素进行拆分
  • 参数split为拆分位置的字串向量,即在哪个字串处开始拆分;该参数默认是正则表达式匹配;若设置fixed= T则表示是用普通文本匹配或者正则表达式的精确匹配。用普通文本来匹配的运算速度要快些。
  • 参数perl的设置和perl的版本有关,表示可以使用perl语言里面的正则表达式。如果正则表达式过长,则可以考虑使用perl的正则来提高运算速度。
  • 参数useBytes表示是否逐字节进行匹配,默认为FALSE,表示是按字符匹配而不是按字节进行匹配。
text <- "We are the world.\nWe are the children!"
text
## [1] "We are the world.\nWe are the children!"
cat(text)  #注意\n被解释称换行符,R里字符串自身也是正则!
## We are the world.
## We are the children!

strsplit(text, " ")
## [[1]]
## [1] "We"         "are"        "the"        "world.\nWe" "are"       
## [6] "the"        "children!"
strsplit(text, "\\s")  #以任意空白符作为分割的位置,注意双反斜线
## [[1]]
## [1] "We"        "are"       "the"       "world."    "We"        "are"      
## [7] "the"       "children!"
class(strsplit(text, "\\s"))
## [1] "list"

strsplit()的返回结果是list类型,如果想将其转换成字符串类型,则可以使用上面提到的unlist()和as.character()。

有一种特殊情况,如果strsplit()的split参数为空字符串的话,得函数的返回结果是一个个字符。

strsplit(text, "")
## [[1]]
##  [1] "W"  "e"  " "  "a"  "r"  "e"  " "  "t"  "h"  "e"  " "  "w"  "o"  "r" 
## [15] "l"  "d"  "."  "\n" "W"  "e"  " "  "a"  "r"  "e"  " "  "t"  "h"  "e" 
## [29] " "  "c"  "h"  "i"  "l"  "d"  "r"  "e"  "n"  "!"

5.字符串查询

字符串的查询或者搜索着要是应用了正则表达式的匹配来完成任务的,R里正方面的函数有grep(),grepl(),regexpr(),gregexpr()和regexec()等。

其中grep()和grepl()两个函数的命令形式如下:
grep(pattern, x, ignore.case= F, perl= F, value= F, fixed= F, useBytes= F, invert= F)
grep(pattern, x, ignore.case= F, perl= F, fixed= F, useBytes= F)由命令形式可以看出,前者返回了向量x中哪个元素匹配了模式pattern(即返回了向量x的某些下标)或者具体哪个元素匹配了模式(通过设置value参数来完成),而后者则返回了一系列逻辑值,其长度等同于向量x的长度,表示向量x中的元素是否匹配了模式。它们都没有提供具体的位置信息,即向量x中的元素在哪个位置匹配了模式。

text <- c("We are the world", "we are the children")
grep("We", text)  #向量text中的哪些元素匹配了单词'We'
## [1] 1
grep("We", text, invert = T)  #向量text中的哪些元素没有匹配单词'We'
## [1] 2
grep("we", text, ignore.case = T)  #匹配时忽略大小写
## [1] 1 2
grepl("are", text)  #向量text中的每个元素是否匹配了单词'We',即只返回TRUE或FALSE
## [1] TRUE TRUE

regexpr(),gregexpr()和regexec()函数同样也可用来进行字符串搜索,与grep()和grepl()不同的是它们返回的结果中包含了匹配的具体位置和字符串长度信息(因此可用于字符串的提取操作中去)。它们的命令形式如下:
regexpr(pattern, text, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)
gregexpr(pattern, text, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)
regexec(pattern, text, ignore.case = FALSE, fixed = FALSE, useBytes = FALSE)

text <- c("We are the world", "we are the children")
regexpr("e", text)
## [1] 2 2
## attr(,"match.length")
## [1] 1 1
## attr(,"useBytes")
## [1] TRUE
class(regexpr("e", text))
## [1] "integer"
gregexpr("e", text)
## [[1]]
## [1]  2  6 10
## attr(,"match.length")
## [1] 1 1 1
## attr(,"useBytes")
## [1] TRUE
## 
## [[2]]
## [1]  2  6 10 18
## attr(,"match.length")
## [1] 1 1 1 1
## attr(,"useBytes")
## [1] TRUE
class(gregexpr("e", text))
## [1] "list"
regexec("e", text)
## [[1]]
## [1] 2
## attr(,"match.length")
## [1] 1
## 
## [[2]]
## [1] 2
## attr(,"match.length")
## [1] 1
class(regexec("e", text))
## [1] "list"

从regexpr()的返回结果看,返回结果是个整数型向量,但是它还具有两个额外的属性(attributes),分别是匹配字段的长度和是否按字节进行匹配;regexpr()的返回结果为-1和1,其中-1表示没有匹配上,1表示text中第2个元素中的第一个字符被匹配上,且匹配字符的长度为2(属性值中提供);gregexpr()的返回结果中包含了全部的匹配结果的位置信息,而regexpr()只返回了向量text里每个元素中第一个匹配的位置信息,gregexpr()的返回结果类型是list类型对象;regexec()的返回结果基本与regexpr()类似,只返回了第一个匹配的位置信息,但其结果是一个list类型的对象,并且列表里面的元素少了一个属性值,即attr(,“useBytes”)。

除了上面的字符串的查询,有时还会用到完全匹配,这是会用到match(),其命令形式如下:match(x, table, nomatch= NAinteger, incomparables)
只有参数x的内容被完全匹配,函数才会返回参数x所在table参数中的下标,否则的话会返回nomatch参数中定义的值(默认是NA)。

text <- c("We are the world", "we are the children", "we")
match("we", text)
## [1] 3
match(2, c(3, 4, 2, 8))
## [1] 3
match("xx", c("abc", "xxx", "xx", "xx"))  #只会返回第一个完全匹配的元素的下标
## [1] 3
match(2, c(3, 4, 2, 8, 2))
## [1] 3
match("xx", c("abc", "xxx"))  # 没有完全匹配的,因此返回NA
## [1] NA

此外还有一个charmatch(),其命令形式类似于match,但从下面的例子来看其行为有些古怪。同样该函数也会返回其匹配字符串所在table中的下标,该函数在进行匹配时,会从table里字符串的最左面(即第一个字符)开始匹配,如果起始位置没有匹配则返回NA;如果同时部分匹配和完全匹配,则会优先选择完全匹配;如果同时有多个完全匹配或者多个部分匹配时,则会返回0;如果以上三个都没有,则返回NA。另外还有一个pmatch(),其功能同charmatch()一样,仅仅写法不同。

charmatch("xx", c("abc", "xxa"))
## [1] 2
charmatch("xx", c("abc", "axx"))  # 从最左面开始匹配
## [1] NA
charmatch("xx", c("xxa", "xxb"))  # 不唯一
## [1] 0
charmatch("xx", c("xxa", "xxb", "xx"))  # 优先选择完全匹配,尽管有两个部分匹配
## [1] 3
charmatch(2, c(3, 4, 2, 8))
## [1] 3
charmatch(2, c(3, 4, 2, 8, 2))
## [1] 0

不知道这样一个奇怪的函数在那里能够用到,真是有点期待!

6.字符串的替换

虽然sub()和gsub()能够提供替换的功能,但其替换的实质是先创建一个对象,然后对原始对象进行重新赋值,最后结果好像是“替换”了一样。(R语言对参数都是传值不传址)

sub()和gsub()的命令形式具体如下:
sub(pattern, replacement, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)
gsub(pattern, replacement, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)

text <- c("we are the world", "we are the children")
sub("w", "W", text)
## [1] "We are the world"    "We are the children"
gsub("w", "W", text)
## [1] "We are the World"    "We are the children"

sub(" ", "", "abc def ghi")
## [1] "abcdef ghi"
gsub(" ", "", "abc def ghi")
## [1] "abcdefghi"

从上面的输出结果可以看出,sub()和gsub()的区别在于,前者只替换第一次匹配的字串(请注意输出结果中world的首字母),而后者会替换掉所有匹配的字串。
注意:gsub()是对向量里面的每个元素进行搜素,如果发现元素里面有多个位置匹配了模式,则全部进行替换,而grep()也是对向量里每个元素进行搜索,但它仅仅知道元素是否匹配了模式(并返回该元素在向量中的下标),但具体元素中匹配了多少次却无法知道。在这里仅仅是为了说明这两者的区别,这在实际中可能不会用到。

text <- c("we are the world", "we are the children")
grep("w", text)  #grep的返回结果为1和2,表示text向量的两个元素中都含有字符w,但text向量的第一个元素里共含有两个字符。
## [1] 1 2
gsub("w", "W", text)
## [1] "We are the World"    "We are the children"

7.字符串的提取

字符串的提取,有些地方类似于字符串的拆分。常用到的提取函数有substr()和substring(),它们都是靠位置来进行提取的,它们自身并不适用正则表达式,但是它们可以结合正则表达式函数regexpr(),gregexpr()和regexec()等可以方便地从文本中提取所需信息。两者的命令形式如下:
substr(x, start, stop)
substring(text, first, last)
x和text为要从中提取的字符串向量,start和first为提取的起始位置向量,stop和last为提取的终止位置向量,但是这两个函数的返回值的长度稍有区别:

  • substr()返回的字符串个数等于第一个向量的长度
  • substring()返回的字符串个数等于其三个参数中长度最长的那个参数的长度
x <- "123456789"
substr(x, c(2, 4), c(4, 5, 8))
## [1] "234"
substring(x, c(2, 4), c(4, 5, 8))
## [1] "234"     "45"      "2345678"

y <- c("12345678", "abcdefgh")
substr(y, c(2, 4), c(4, 5, 8))
## [1] "234" "de"
substring(y, c(2, 4), c(4, 5, 8))
## [1] "234"     "de"      "2345678"

从上面的输出结果来,向量x的长度为1,substr()不管后面的两个参数的长度如何,它只会用到这两个参数的第一个数值,即分别为2和4,表示提取的起始和终止位置分别为2和4,返回的结果则是字符串“234”。而用substring()来提取时,则会依据参数最长的last参数,此外还需要注意的是first和last两个参数的长度不等,这时会用到R里面的“短向量循环”原则,参数first会自动延长为c(2, 4, 2),函数会依次提取从2到4,从4到5,从2到8这三个字符串。

用substing()可以很方便地把DNA或RNA序列进行翻译(三个碱基转换成一个密码子)。

dna <- paste(sample(c("A", "G", "C", "T"), 12, replace = T), collapse = "")
dna
## [1] "ATAACGCGTGGG"
substring(dna, seq(1, 10, by = 3), seq(3, 12, by = 3))
## [1] "ATA" "ACG" "CGT" "GGG"

8.字符串的定制输出

这个内容有点类似于字符串的连接。这里用到了strtrim(),用于将字符串修剪到特定的显示宽度,其命令形式如下:
strtrim(x, width)
该函数返回的字符串向量的长度等于参数x的长度。

strtrim(c("abcde", "abcde", "abcde"), c(1, 5, 10))
## [1] "a"     "abcde" "abcde"
strtrim(c(1, 123, 12345), 4)  #短向量循环
## [1] "1"    "123"  "1234"

strtrim()会根据width参数提供的数字来修剪字符串,若width提供的数字大于字符串的字符数的话,则该字符串会保持原样,不会增加空格之类的东西。

strwrap()会把字符串当成一个段落来处理(不管段落中是否有换行),按照段落的格式进行缩进和分行,返回结果就是一行行的字符串,其命令形式如下:
strwrap(x, width, indent= 0, exdent= 0, prefix= “”, simplify= T, initial= prefix)
函数返回结果中的每一行的字符串中的字符数目等于参数width。

string <- "Each character string in the input is first split into\n paragraphs (or lines containing whitespace only). The paragraphs are then formatted by breaking lines at word boundaries."
string
## [1] "Each character string in the input is first split into\n paragraphs (or lines containing whitespace only). The paragraphs are then formatted by breaking lines at word boundaries."
cat(string)
## Each character string in the input is first split into
##  paragraphs (or lines containing whitespace only). The paragraphs are then formatted by breaking lines at word boundaries.
strwrap(string)  #直接将换行符忽略了
## [1] "Each character string in the input is first split into paragraphs"
## [2] "(or lines containing whitespace only). The paragraphs are then"   
## [3] "formatted by breaking lines at word boundaries."
strwrap(string, width = 40, indent = 4)  #首行缩进
## [1] "    Each character string in the input"
## [2] "is first split into paragraphs (or"    
## [3] "lines containing whitespace only). The"
## [4] "paragraphs are then formatted by"      
## [5] "breaking lines at word boundaries."
strwrap(string, width = 40, exdent = 4)  #除了首行的其余行缩进
## [1] "Each character string in the input is" 
## [2] "    first split into paragraphs (or"   
## [3] "    lines containing whitespace only)."
## [4] "    The paragraphs are then formatted" 
## [5] "    by breaking lines at word"         
## [6] "    boundaries."
strwrap(string, width = 40, simplify = F)  # 返回结果是个列表,而不再是个字符串向量
## [[1]]
## [1] "Each character string in the input is"
## [2] "first split into paragraphs (or lines"
## [3] "containing whitespace only). The"     
## [4] "paragraphs are then formatted by"     
## [5] "breaking lines at word boundaries."
strwrap(string, width = 40, prefix = "******")
## [1] "******Each character string in the"    
## [2] "******input is first split into"       
## [3] "******paragraphs (or lines containing" 
## [4] "******whitespace only). The paragraphs"
## [5] "******are then formatted by breaking"  
## [6] "******lines at word boundaries."

猜你喜欢

转载自blog.csdn.net/XIUXIU179/article/details/80984149