正则表达式及R字符串处理之终结版

http://yphuang.github.io/blog/2016/03/15/regular-expression-and-strings-processing-in-R/

 

0.动机:为什么学习字符串处理

传统的统计学教育几乎没有告诉过我们,如何进行文本的统计建模分析。然而,我们日常生活中接触到的大部分数据都是以文本的形式存在。文本分析与挖掘在业界中也有着非常广泛的应用。

由于文本数据大多属于非结构化的数据,要想对文本数据进行传统的统计模型分析,必须要经过层层的数据清洗与整理。

今天我们要介绍的『正则表达式及R字符串处理』就是用来干这一种脏活累活的。

与建立酷炫的模型比起来,数据的清洗与整理似乎是一种低档次的工作。如果把建立模型类比于高级厨师的工作,那么,数据清洗无疑是类似切菜洗碗打扫卫生的活儿。然而想要成为资深的『数据玩家』,这种看似低档次的工作是必不可少的,并且,这种工作极有可能将占据你整个建模流程的80%的时间。

如果我们能够掌握高效的数据清洗工具,那么我们将拥有更多的时间来进行模型选择和参数调整,使得我们的模型更加合理有效。

此外,对于不需要进行文本建模分析的同学,掌握文本处理的工具也将对减轻你的工作负担大有益处。下面,我举几个我自身经历的『文本处理工具让生活更美好』的例子:

可见,我们可以用到文本处理工具的场景还是非常多的,如批量文件名修改、批量字符替换、大量邮件或html文件处理等。

下面,我将通过一个例子,展示R字符串处理的大致功能。接着,介绍正则表达式的基础概念。然后,介绍R字符串处理中一个非常好用的拓展包stringr,并接着介绍一些文件编码处理相关的函数。最后,通过一两个案例展示字符串处理的真实应用场景。


1.A toy example ——初步认识R中的字符串处理

为了先给大家一个关于R字符串处理的大体认识,我们使用R中自带的一个数据集USArrests进行函数功能演示。

先看看数据集的结构。

# take a glimpse 
head(USArrests)

字符串子集提取:获得州的简称

# 获得州名 
states = rownames(USArrests) # 方法一:substr() substr(x = states, start = 1, stop = 4) # 方法二:abbreviate()  abbreviate(states,minlength = 5)

字符统计:获得名字最长的州名

# get number of characters in each state name

state_chars <- nchar(states) # hist hist(nchar(states),main = "Histogram", xlab = "number of charaters in US State names") # longest state's name states[which(state_chars == max(state_chars))]

注意:nchar()与length()的区别

字符串匹配:含某些字母的州名

# get states names with 'w'
grep(pattern = "w", x = states, value = TRUE) ###########################  # get states names with 'W' OR 'w'  ## Method 1: grep(pattern = "[wW]", x = states, value = TRUE) ## Method 2: grep(pattern = "w", x = tolower(states),value = TRUE) ## Method 3: grep(pattern = "W", x = toupper(states), value = TRUE) ## Method 4: grep(pattern = "w",x = states, ignore.case = TRUE, value = TRUE)

字符统计:某些字母个数统计

library(stringr) # total number of a's str_count(states,"a") ################## # number of vowels  # vector of vowels vowels <- c("a","e","i","o","u") # vector for storing results num_vowels <- vector(mode = "integer",length = 5) # calculate for(i in seq_along(vowels)){ num_aux <- str_count(tolower(states),vowels[i]) num_vowels[i]<-sum(num_aux) } # add names names(num_vowels)<-vowels # total number of vowels num_vowels # barplot barplot(num_vowels, main = "number of vowels in USA States names")

2.正则表达式

正则表达式是对字符串类型数据进行匹配判断,提取等操作的一套逻辑公式。

处理字符串类型数据方面,高效的工具有Perl和Python。如果我们只是偶尔接触文本处理任务,则学习Perl无疑成本太高;如果常用Python,则可以利用成熟的正则表达式模块:re库;如果常用R,则使用Hadley大神开发的stringr包则已经能够游刃有余。

下面,我们先简要介绍重要并通用的正则表达式规则。接着,总结一下stringr包中需要输入正则表达式参数的字符处理函数。

元字符(Metacharacters)

大部分的字母和所有的数字都是匹配他们自身的正则表达式。然而,在正则表达式的语法规定中,有12个字符被保留用作特殊用途。他们分别是:

[ ] \ ^ $ . | ? * + ( )

如果我们直接进行对这些特殊字符进行匹配,是不能匹配成功的。正确理解他们的作用与用法,至关重要。

library(stringr) metaChar = c("$","*","+",".","?","[","^","{","|","(","\\") grep(pattern="$", x=metaChar, value=TRUE) grep(pattern="\\", x=metaChar, value=TRUE) grep(pattern="(", x=metaChar, value=TRUE) gsub(pattern="|", replacement=".", "gsub|uses|regular|expressions") strsplit(x="strsplit.aslo.uses.regular.expressions", split=".")

它们的作用如下:

  • [ ]:括号内的任意字符将被匹配;
# example
grep(pattern = "[wW]", x = states, value = TRUE)
  • \:具有两个作用:
    • 1.对元字符进行转义(后续会有介绍)
    • 2.一些以\开头的特殊序列表达了一些字符串组
strsplit(x="strsplit.aslo.uses.regular.expressions", split=".") # compare strsplit(x="strsplit.aslo.uses.regular.expressions", split="\\.") ################ # function 2: library(stringr) str_extract_all(string = "my cridit card number: 34901358932236",pattern = "\\d")
  • ^:匹配字符串的开始.将^置于character class的首位表达的意思是取反义。如[^5]表示匹配除了”5”以外的任何字符。
# function 1
test_vector<-c("123","456","321") library(stringr) str_extract_all(test_vector,"3") str_extract_all(test_vector,"^3") # function 2 str_extract_all(test_vector,"[^3]")
  • $:匹配字符串的结束。但将它置于character class内则消除了它的特殊含义。如[akm$]将匹配’a’,’k’,’m’或者’$’.
# function 1
test_vector<-c("123","456$","321") library(stringr) str_extract_all(test_vector,"3$") # function 2 str_extract_all(test_vector,"[3$]")
  • .:匹配除换行符以外的任意字符。
str_extract_all(string = c("regular.expressions\n","\n"), pattern ="\\.")
  • |:或者
test_vector2<-c("AlphaGo实在厉害!","alphago是啥","阿尔法狗是一条很凶猛的狗。") str_extract_all(string = test_vector2, pattern ="AlphaGo|阿尔法狗")
  • ?:前面的字符(组)是可有可无的,并且最多被匹配一次
str_extract_all(string = c("abc","ac","bc"),pattern = "ab?c")
  • *:前面的字符(组)将被匹配零次或多次
str_extract_all(string = c("abababab","abc","ac"),pattern = "(ab)*")
  • +:前面的字符(组)将被匹配一次或多次
str_extract_all(string = c("abababab","abc","ac"),pattern = "(ab)+")
  • ( ):表示一个字符组,括号内的字符串将作为一个整体被匹配。
str_extract_all(string = c("ababc","ac","cde"),pattern = "(ab)?c") str_extract_all(string = c("abc","ac","cde"),pattern = "ab?c")

重复

代码含义说明
?重复零次或一次
*重复零次或多次
+重复一次或多次
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n次到m次
str_extract_all(string = c("abababab","ababc","ababababc"),pattern = "(ab){2,3}")

转义

如果我们想查找元字符本身,如”?”和”*“,我们需要提前告诉编译系统,取消这些字符的特殊含义。这个时候,就需要用到转义字符\,即使用\?\*.当然,如果我们要找的是\,则使用\\进行匹配。

strsplit(x="strsplit.aslo.uses.regular.expressions", split=".") # compare strsplit(x="strsplit.aslo.uses.regular.expressions", split="\\.")

注:R中的转义字符则是双斜杠:\\

R中预定义的字符组

代码含义说明
[:digit:]数字:0-9
[:lower:]小写字母:a-z
[:upper:]大写字母:A-Z
[:alpha:]字母:a-z及A-Z
[:alnum:]所有字母及数字
[:punct:]标点符号,如. , ;
[:graph:]Graphical characters,即[:alnum:]和[:punct:]
[:blank:]空字符,即:Space和Tab
[:space:]Space,Tab,newline,及其他space characters
[:print:]可打印的字符,即:[:alnum:],[:punct:]和[:space:]
library(stringr) str_extract_all(string = "my cridit card number: 34901358932236",pattern = "\\d")

代表字符组的特殊符号

代码含义说明
\w字符串,等价于[:alnum:]
\W非字符串,等价于[^[:alnum:]]
\s空格字符,等价于[:blank:]
\S非空格字符,等价于[^[:blank:]]
\d数字,等价于[:digit:]
\D非数字,等价于[^[:digit:]]
\bWord edge(单词开头或结束的位置)
\BNo Word edge(非单词开头或结束的位置)
\<Word beginning(单词开头的位置)
\>Word end(单词结束的位置)

3.stringr字符串处理函数对比学习

stringr包中的重要函数

函数功能说明R Base中对应函数
使用正则表达式的函数  
str_extract()提取首个匹配模式的字符regmatches()
str_extract_all()提取所有匹配模式的字符regmatches()
str_locate()返回首个匹配模式的字符的位置regexpr()
str_locate_all()返回所有匹配模式的字符的位置gregexpr()
str_replace()替换首个匹配模式sub()
str_replace_all()替换所有匹配模式gsub()
str_split()按照模式分割字符串strsplit()
str_split_fixed()按照模式将字符串分割成指定个数-
str_detect()检测字符是否存在某些指定模式grepl()
str_count()返回指定模式出现的次数-
其他重要函数  
str_sub()提取指定位置的字符regmatches()
str_dup()丢弃指定位置的字符-
str_length()返回字符的长度nchar()
str_pad()填补字符-
str_trim()丢弃填充,如去掉字符前后的空格-
str_c()连接字符paste(),paste0()

可见,stringr包中的字符处理函数更丰富和完整,并且更容易记忆。

文本文件的读写

这里的文本文件指的是非表格式的文件,如纯文本文件,html文件。文本文件的读取可以使用readLines()scan()函数。一般需要通过encoding = 参数设置文件内容的编码方式。

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

文本文件的写出可以使用cat()writeLines()函数。

# 假设要保存当前环境中的R变量text
# sep参数指定要保存向量里的元素的分割符号。
cat(text, file = "file.txt", sep = "\n") writeLines(text, con = "file.txt", sep = "\n", useBytes = F)

字符统计及字符翻译

x<- c("I love R","I'm fascinated by Statisitcs") ################## ## 字符统计  # nchar nchar(x) # str_count library(stringr) str_count(x,pattern = "") str_length(x) ######################  DNA <- "AgCTaaGGGcctTagct" ## 字符翻译:大小写转换 tolower(DNA) toupper(DNA) ## 字符翻译:符号替换(逐个替换) # chartr chartr("Tt", "Uu", DNA) #将T碱基替换成U碱基  # 注意:与str_replace()的区别  library(stringr) str_replace_all(string = DNA,pattern = "T",replacement = "U") %>% str_replace_all(string = .,pattern = "t",replacement = "u")

字符串连接

# paste
paste("control",1:3,sep = "_") # str_c() library(stringr) str_c("control",1:3,sep = "_")

字符串拆分

# strsplit
text <- "I love R.\nI'm fascinated by Statisitcs." cat(text) strsplit(text,split = " ") strsplit(text,split = "\\s") # str_split library(stringr) str_split(text,pattern = "\\s")

字符串查询

字符串的查询或者搜索应用了正则表达式的匹配来完成任务. R Base 包含的字符串查询相关的函数有grep(),grepl(),regexpr(),gregexpr()和regexec()等。

################################# ## 包含匹配  # grep x<- c("I love R","I'm fascinated by Statisitcs","I") grep(pattern = "love",x = x) grep(pattern = "love",x = x,value = TRUE) grepl(pattern = "love",x = x) # str_detect  str_detect(string = x, pattern = "love") ################################# # # match,完全匹配, 常用的 %in% 由match()定义 match(x = "I",table = x) "I'm" %in% x

字符串替换

sub()和gsub()能够提供匹配替换的功能,但其替换的实质是先创建一个对象,然后对原始对象进行重新赋值,最后结果好像是“替换”了一样。

sub()和gsub()的区别在于,前者只替换第一次匹配的字串(请注意输出结果中world的首字母),而后者会替换掉所有匹配的字串。

也可以使用substr和substring对指定位置进行替换。

##################################### ## 匹配替换  test_vector3<-c("Without the vowels,We can still read the word.") # sub sub(pattern = "[aeiou]",replacement = "-",x = test_vector3) # gsub gsub(pattern = "[aeiou]",replacement = "-",x = test_vector3) # str_replace_all  str_replace_all(string = test_vector3,pattern = "[aeiou]", replacement = "-") ########################################## ## 指定位置替换 

字符串提取

常用到的提取函数有substr()和substring(),它们都是靠位置来进行提取的,它们自身并不适用正则表达式,但是它们可以结合正则表达式函数regexpr(),gregexpr()和regexec()等可以方便地从文本中提取所需信息。

stringr包中的函数str_substr_dup可以通过位置提取,而str_extractstr_match可以通过正则表达式提取。

substr("abcdef", start = 2, stop = 4) substring("abcdef", first = 1:6, last = 2:7) str_sub("abcdef",start = 2, end = 4) str_sub("abcdef",start = 1:6, end = 1:6) ################################  text_weibo<- c("#围棋人机大战# 【人工智能攻克围棋 AlphaGo三比零完胜李世石】","谷歌人工智能AlphaGo与韩国棋手李世石今日进行了第三场较量","最终AlphaGo战胜李世石,连续取得三场胜利。接下来两场将沦为李世石的“荣誉之战。") # str_match_all,返回的列表中的元素为矩阵  str_match_all(text_weibo,pattern = "#.+#") str_match_all(text_weibo, pattern = "[a-zA-Z]+") # str_extract_all,返回的列表中的元素为向量 str_extract_all(text_weibo,pattern = "#.+#") str_extract_all(text_weibo, pattern = "[a-zA-Z]+")

字符串定制输出

这个内容有点类似于字符串的连接。R中相应的函数为strtrim(),用于将字符串修剪到特定的显示宽度。stringr中相应的函数为:str_pad().

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

strtrim(c("abcde", "abcde", "abcde"),width = c(1, 5, 10)) str_pad(string = c("abcde", "abcde", "abcde"),width = c(1, 5, 10),side = "right")

strwrap()会把字符串当成一个段落来处理(不管段落中是否有换行),按照段落的格式进行缩进和分行,返回结果就是一行行的字符串。

而str_wrap()不对文本直接切割成向量,而是在文本内容中插入了缩进或分行的标识符。

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." strwrap(x = string, width = 30) #str_wrap str_wrap(string = string,width = 30) cat(str_wrap(string = string, width = 30))

4.字符编码相关的重要函数

windows下处理字符串类型数据最头疼的无疑是编码问题了。这里介绍几个编码转换相关的函数。

函数功能说明
iconv()转换编码格式
Encoding()查看编码格式;或者指定编码格式
tau::is.locale()tests if the components of a vector of character are in the encoding of the current locale
tau::is.ascii() 
tau::is.utf8()tests if the components of a vector of character are true UTF-8 strings

虽然查看编码方式已经有Encoding()函数,但是这个函数往往在很多时候都不灵,经常返回恼人的“Unknow”。而火狐浏览器进行网页文本编码识别的一个 c++ 库universalchardet ,可以识别的编码种类较多。文锋写了一个相应的R包接口,专用于文件编码方式检测,具体请参考:checkenc - 自动文本编码识别

devtools::install_github("qinwf/checkenc") library(checkenc) checkenc("2016-03-10-regular-expression-and-strings-processing-in-R.html") Encoding("2016-03-10-regular-expression-and-strings-processing-in-R.html")

5.应用案例

最后,给大家展示一个小小的爬虫案例:爬取豆瓣T250中的电影信息进行分析。这里出于练习的目的刻意使用了字符串处理函数,在实际的爬虫中,有更方便快捷的实现方式。

本案例改编自肖凯老师的博客在R语言中使用正则表达式,原博客使用R Base中的函数进行处理字符串,这里已经全部更改为stringr中的函数进行处理。

library(stringr) library(dplyr) url <-'http://movie.douban.com/top250?format=text' # 获取网页原代码,以行的形式存放在web变量中 setInternet2() web <- readLines(url,encoding="UTF-8") # 找到包含电影名称的行 name<-str_extract_all(string = web, pattern = '<span class="title">.+</span>') movie.names_line <- unlist(name) # 用正则表达式来提取电影名 movie.names <- str_extract(string = movie.names_line, pattern = ">[^&].+<") %>% str_replace_all(string = ., pattern = ">|<",replacement = "") movie.names<- na.omit(movie.names) # 获取评价人数 Rating<- str_extract_all(string = web,pattern = '<span>[:digit:]+人评价</span>') Rating.num_line<-unlist(Rating) Rating.num<- str_extract(string = Rating.num_line, pattern = "[:digit:]+") %>% as.numeric(.) #获取评价分数 Score_line<-str_extract_all(string = web, pattern = '<span class="rating_num" property="v:average">[\\d\\.]+</span>') Score_line<- unlist(Score_line) Score<- str_extract(string = Score_line, pattern = '\\d\\.\\d') %>% as.numeric(.) # 数据合并  MovieData<- data.frame(MovieName = movie.names, RatingNum =

转载于:https://www.cnblogs.com/nkwy2012/p/8601562.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值