Notes | R正则表达式

Posted 女王的code

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Notes | R正则表达式相关的知识,希望对你有一定的参考价值。

读书笔记

Ch.8:

基于R语言的自动数据收集


Web上的内容主要是无结构的文本,网络抓取的一项核心任务是从文本数据堆中采集和我们研究问题相关的信息,通常经过三个步骤来处理,一是采集无结构文本;二是判定寻找的信息背后有什么重复规律;三是把这些规律运用到无结构文本中,以提取信息。以html网页为例,理想状况下可以利用Xpath来提取,但有时关键信息隐藏在最底层的值里面,或者散布在网页中的各个部分,使得对网页结构的分析方法失去作用,而正则表达式则提供了在文本中系统化分析规律的语法。

1. 正则表达式

A regular expression is a pattern that describes a set of strings. Two types of regular expressions are used in R, extended regular expressions (the default) and Perl-like regular expressions used by perl = TRUE. There is also fixed = TRUE which can be considered to use a literal regular expression.(Regular Expressions as used in R http://stat.ethz.ch/R-manual/R-patched/library/base/html/regex.html)

R中有两种正则表达式风格,扩展基本正则表达式和Perl正则表达式,在下面的例子中完全基于扩展基本正则表达式。

1.1 严格的字符匹配

目的

从字符串中提取子串

常用函数

stringr{str_extract}

stringr{str_ extract_ all}

 
   
   
 
  1. 函数定义:

  2. str_extract(string, pattern)

  3. str_extract_all(string, pattern, simplify = FALSE)

  4. 参数列表:

  5. string: 字符串,字符串向量。

  6. pattern: 匹配字符。

  7. simplify: 返回值,TRUE返回matrixFALSE返回字符串向量


 
   
   
 
  1. library(stringr)

  2. test<-"1. A small sentence. - 2. Another tiny sentence. -3. some sentences"

  3. data<-str_extract(test,"sentence")

  4. data

  5. [1] "sentence"

  6. data2<-str_extract(test,"tence")

  7. data2

  8. [1] "tence"

  9. data2<-str_extract(test,"e")

  10. data2

  11. [1] "e"

  12. data2<-str_extract(test,"SENTENCE")

  13. data2

  14. [1] NA

stringr{str_ extract}会在一个给定的字符串里返回给定正则表达式匹配的第一个实例,相对于str_ extract,该函数能够提取出字符串中每一个匹配的结果:

 
   
   
 
  1. data3<-str_extract_all(test,"sentence")

  2. data3<-unlist(data3)

  3. data3

  4. [1] "sentence" "sentence" "sentence"

  5. > data3<-str_extract_all(test,"sentence",simplify = TRUE)

  6. > data3

  7.     [,1]       [,2]       [,3]      

  8. [1,] "sentence" "sentence" "sentence"

由于str_ extract_ all通常可以对多个字符串进行调用,所以结果通常作为一个列表返回,每个列表元素提供了针对其中一个字符串的结果,上面例子由于是一个长度为1的字符向量,所以返回长度为1的列表,我们可以调用unlist进行解析,此外,我们也可以修改参数simplify将结果以矩阵形式输出。

 
   
   
 
  1. test1<-"panda is a PANDA"

  2. test2<-"panda is one of animals"

  3. test3<-"panda likes bamboo"

  4. data4<-str_extract_all(c(test1,test2,test3),"PANDA")

  5. data4

  6. [[1]]

  7. [1] "PANDA"  #区分大小写

  8. [[2]]

  9. character(0) #无匹配结果

  10. [[3]]

  11. character(0)

以上案例创建了一个包含三个字符串对象的向量,使用strextractall函数提取符合的实力,函数返回一个与向量长度相同的列表,列表中的每个元素包含了对应字符串的匹配结果。

使用正则表达式并不局限于匹配单词,一个字符串无非是一个字符的序列,因此我们可以匹配单个字母、词根或者字母字符和空格的混合等等

常用符号

  • 位置标记:(^)($)

有时我们关系的不是随便在字符串里边找到一个匹配,而是对字符串里的具体位置感兴趣,可以使用两个简单的符号表示位置,其中(^)标记字符串的起点,($)标记了字符串的结尾:

 
   
   
 
  1. str_extract_all(c(test1,test2,test3),"small")

  2. [[1]]

  3. [1] "small"

  4. [[2]]

  5. [1] "small"

  6. [[3]]

  7. [1] "small"

  8. str_extract_all(c(test1,test2,test3),"^small")

  9. [[1]]

  10. character(0)

  11. [[2]]

  12. [1] "small"

  13. [[3]]

  14. character(0)

  15. str_extract_all(c(test1,test2,test3),"small$")

  16. [[1]]

  17. character(0)

  18. [[2]]

  19. character(0)

  20. [[3]]

  21. [1] "small"

  • 管道标记:(|)
    另一个对正则表达式的有力补充是管道,表示为(|),视为or操作符,函数会返回对于管道前后表达式的所有匹配。

 
   
   
 
  1. test<-"1. A small sentence. - 2. Another tiny sentence. -3. some sentences"

  2. str_extract_all(test,"Another|small")

  3. [[1]]

  4. [1] "small"   "Another"

1.2 广义的正则表达式

  • 匹配任意一个字符(.)

 
   
   
 
  1. test<-"bag big blog belong"

  2. str_extract_all(test,"b.g")

  3. [[1]]

  4. [1] "bag" "big"

  • 匹配其中一个字符([])(-)

 
   
   
 
  1. test<-"bag big blog beg"

  2. str_extract_all(test,"b[ae]g")

  3. [[1]]

  4. [1] "bag" "beg"

  5. str_extract_all(test,"b[a-k]g")

  6. [[1]]

  7. [1] "bag" "big" "beg"

  8. str_extract_all(test,"b[a-g]g")

  9. [[1]]

  10. [1] "bag" "beg"

利用(-)可以实现在字符范围内进行匹配,如a-g表示从a到g的字符都是合法的匹配。

除匹配字母外,也可以在正则表达式里包括数字、标点和空格,相类似,它们特可以放在字符里

 
   
   
 
  1. test<-"1. A small sentence. - 2. Another tiny sentence. -3. some sentences"

  2. str_extract_all(test,"[vw. ]")

  3. [[1]]

  4. [1] "." " " " " " " "." " " " " "." " " " " " " "." " " "." " " " "

  • 字符类

在文本内部进行匹配的字符集合里,有一些是典型的,为方便起见,在R里已经预定义了一些常用的 字符类

Notes | R正则表达式

为了利用预定义的字符类,我们必须将其放在 方括号[]内,否则R就会认为我们指定了一个由[]其中的字符组成的字符类:

 
   
   
 
  1. test <- "1. A small sentence. - 2. Another tiny sentence."

  2. unlist(str_extract_all(test, "[[:punct:]]"))

  3. [1] "." "." "-" "." "."

  4. test <- "1. A small sentence. - 2. Another tiny sentence."

  5. unlist(str_extract_all(test, "[[:punct:]ABC]"))

  6. [1] "." "A" "." "-" "." "A" "."

  7. unlist(str_extract_all(test, "[^[:alnum:]]"))

  8. [1] "." " " " " " " "." " " "-" " " "." " " " " " " "."

字符类另一种巧妙的用法是通过在字符类起始位置加入(^)来反转其含义,这样函数就会匹配除了该字符之外的所有内容。

  • 量化字符标记({})(+)等

在字符后面的{}里添加数字表示该字符固定次数的重复,像aaaa这样的序列可以简化为a{4};加号(+)表示之前的条目必须匹配一次或多次,我们可以利用“"A.+sentence”提取以A开头,sentence结尾的序列。

 
   
   
 
  1. test <- "1. A small sentence. - 2. Another tiny sentence."

  2. str_extract_all(test, "s[[:alpha:]][[:alpha:]][[:alpha:]]")

  3. [[1]]

  4. [1] "smal" "sent" "sent"

  5. str_extract_all(test, "s[[:alpha:]]{3}")

  6. [[1]]

  7. [1] "smal" "sent" "sent"

  8. str_extract_all(test, "A.+sentence")

  9. [[1]]

  10. [1] "A small sentence. - 2. Another tiny sentence"

Notes | R正则表达式

R应用是贪婪量化模式,这意味着程序会尽量提取能匹配前面字符的最大合法序列,因为匹配的是任何字符,所以上面的函数会返回在sentence序列之前的由任何字符构成的最大序列,我们可以添加(?)来改变这种行为,表达只想找到sentence序列之前的由任意字符构成的最短序列,(?)的含义就是它之前的条目是可选的,最多能匹配一次:

 
   
   
 
  1. str_extract(test, "A.+?sentence")

  2. [1] "A small sentence"

为了不局限于对单个字符应用量化符,我们要把其放在括号里,在下例中,我们要求第一个字符可以是任意字符,第二个和第三个必须是e和n,要求函数给出所有该序列至少1次最多5次的所有实例,符合该请求的最长合法序列可以是3*5=15个字符长度,注意没有括号情况下表示最后一个字母n必须出现的次数。

 
   
   
 
  1. test <- "1. A small sentence. - 2. Another tiny sentenmen.- 3. A long sentensennen."

  2. unlist(str_extract_all(test, "(.en){1,5}"))

  3. [1] "senten"       "sentenmen"    "sentensennen"

  4. unlist(str_extract_all(test, "(.en){3,5}"))

  5. [1] "sentenmen"    "sentensennen"

  6. unlist(str_extract_all(test, ".en{1,5}"))

  7. [1] "sen"  "ten"  "sen"  "ten"  "men"  "sen"  "ten"  "senn"

  8. unlist(str_extract_all(test, ".en{2,5}"))

  9. [1] "senn"

以上学习的都属于元字符(metacharacter),主要包括: .; |; (); []; {}; ^; $; *; +; ?; -。 为了在字符串中对这些元字符进行匹配,我们需要在前面加入双斜杠,或者使用fixed()函数:

 
   
   
 
  1. test <- "1. A small sentence. - 2. Another tiny sentence."

  2. unlist(str_extract_all(test, "\\."))

  3. [1] "." "." "." "."

  4. unlist(str_extract_all(test, fixed(".")))

  5. [1] "." "." "." "."

Notes | R正则表达式

 
   
   
 
  1. test <- "1. A small sentence. - 2. Another tiny sentence."

  2. unlist(str_extract_all(test, "\\w+"))

  3. [1] "1"        "A"        "small"    "sentence" "2"        "Another"  "tiny"     "sentence"

  4. unlist(str_extract_all(test, "e\\b"))

  5. [1] "e" "e"

例如上例中的\w字符表示能在我们的实际例子中匹配任何单词字符,\b表示匹配处于单词结尾位置的e。 此外,我们可以对之前已经在正则表达式里匹配过的一个序列进行匹配,这个叫做“反向引用”(backreferencing):

 
   
   
 
  1. test <- "1. A small sentence. - 2. Another tiny sentence."

  2. str_extract(test, "([[:alpha:]]).+?\\1")

  3. [1] "A small sentence. - 2. A"

  4. str_extract(test, "(\\b[b-z]+\\b).+?\\1")

  5. [1] "sentence. - 2. Another tiny sentence"

第一个例子中,[[:alpha:]]表示匹配任一字符,.+?\1表示前面匹配出来的字符下一次出现;第二个例子[b-z]+表示匹配所有长度大于或等于1且不包含字母a的小写字母序列,前面的1. A 不符合要求,第一个匹配该表达式的子字符串应该是ll,但+量化符是贪婪的,它会尝试抓取最长的合法序列,也就是ll而不是l,这不符合我们的要求,为了排除这个结果,我们添加\b表示一个单词的起始和结尾,匹配上字符串的第一部分是sentence,然后利用\1在字符串里查找这个子串的下一次出现(若为\2表示有两个反向引用,一直到9)

2. 一个实例:提取电话目录中的人名和号码

 
   
   
 
  1. # 源数据

  2. rawdata <- "555-1239Moe Szyslak(636) 555-0113Burns, C. Montgomery555-6542Rev. Timothy Lovejoy555 8904Ned Flanders636-555-3226Simpson, Homer5543642Dr. Julius Hibbert"  


 
   
   
 
  1. # 提取人名

  2. name <- unlist(str_extract_all(rawdata, "[[:alpha:]., ]{2,}"))

  3. name

  4. [1] "Moe Szyslak"          "Burns, C. Montgomery" "Rev. Timothy Lovejoy" "Ned Flanders"        

  5. [5] "Simpson, Homer"       "Dr. Julius Hibbert"  

  6. name1 <- unlist(str_extract_all(rawdata, "[[:alpha:]., ]"))

  7. name1

  8. [1] "M" "o" "e" " " "S" "z" "y" "s" "l" "a" "k" " " "B" "u" "r" "n" "s" "," " " "C" "." " " "M" "o" "n" "t"

  9. [27] "g" "o" "m" "e" "r" "y" "R" "e" "v" "." " " "T" "i" "m" "o" "t" "h" "y" " " "L" "o" "v" "e" "j" "o" "y"

  10. [53] " " "N" "e" "d" " " "F" "l" "a" "n" "d" "e" "r" "s" "S" "i" "m" "p" "s" "o" "n" "," " " "H" "o" "m" "e"

  11. [79] "r" "D" "r" "." " " "J" "u" "l" "i" "u" "s" " " "H" "i" "b" "b" "e" "r" "t"

  12. name2 <- unlist(str_extract_all(rawdata, "[[:alpha:]., ]{1,}"))

  13. name2

  14. [1] "Moe Szyslak"          " "                    "Burns, C. Montgomery" "Rev. Timothy Lovejoy"

  15. [5] " "                    "Ned Flanders"         "Simpson, Homer"       "Dr. Julius Hibbert"  

观察人名可以发现包含两条规律:

  • 人名中包含大写和小写字母字符

  • 人名中包含句号、逗号和空格



 
   
   
 
  1. # 提取号码

  2. phone <- unlist(str_extract_all(rawdata, "\\(?(\\d{3})?\\)?(-| )?\\d{3}(-| )?\\d{4}"))

  3. phone

  4. [1] "555-1239"       "(636) 555-0113" "555-6542"       "555 8904"       "636-555-3226"   "5543642"    

号码提取相比人名要复杂一点,分析号码形式可以发现:

  • 第一:区号为三位,可能存在可能不存在

  • 第二:区号前后可能存在括号、破折号或空格


因此,针对第一条,\\d{3}表示三位区号,(\\d{3})?表示区号可以忽略;针对第二条,\\(?和\\)?表示括号可以忽略,(-| )?表示破折号和空格可以忽略;针对第三条,\\d{3}提取前三位数字,(-| )?代表中间的符号可以忽略,\\d{4}提取后面四位数字。

和我一起读书吧

以上是关于Notes | R正则表达式的主要内容,如果未能解决你的问题,请参考以下文章

如何使用正则表达式进行多次替换?

markdown PHPExcel Notes和代码片段

通过 Java 正则表达式提取 semver 版本字符串的片段

利用正则表达式实现python强口令检测

text 正则表达式片段

markdown 正则表达式模式片段