R语言-正则表达式

R语言中正则表达式

内容概览

  有时候我们要处理的是非结构化的数据,例如网页或是电邮资料,那么就需要用R来抓取所需的字符串,整理为进一步处理的数据形式。R语言中有一整套可以用来处理字符的函数,在之前的 博文 中已经有所涉及。但真正的要用好字符处理函数,则不得不用到正则表达式。 正则表达式(Regular Expression、regexp) 是指一种用来描述一定数量文本的模式。熟练掌握正则表达式能使你随心所欲的操作文本来达成目标。其实学习正则表达式并没有想像中的那么困难。最好方法是从例子开始,然后多练习,多使用。网络上已经有许多不错的参考资料,例如 这篇 或 那篇 。本文假设你对正则表达式有了基本的了解,下面我们来看看如何在R里面来使用它。

  R语言处理文本的能力虽然不强,但适当用用还是可以大幅提高工作效率的,而且有些文本操作还不得不用。高效处理文本少不了正则表达式(regular expression),虽然R在这方面先天不高效,但它处理字符串的绝大多数函数都使用正则表达式。

  • 正则表达式简介:

      正则表达式不是R的专属内容,这里也只简单介绍,更详细的内容请查阅其他文章。正则表达式是用于描述/匹配一个文本集合的表达式。

      1.  所有英文字母、数字和很多可显示的字符本身就是正则表达式,用于匹配它们自己。比如 \'a\' 就是匹配字母 \'a\' 的正则表达式
      
      2.  一些特殊的字符在正则表达式中不在用来描述它自身,它们在正则表达式中已经被“转义”,这些字符称为“元字符”。perl类型的正则表达式中被转义的字符有:. \ | ( ) [ ] { } ^ $ * + ?。被转义的字符已经有特殊的意义,如点号 . 表示任意字符;方括号表示选择方括号中的任意一个(如[a-z] 表示任意一个小写字符);^ 放在表达式开始出表示匹配文本开始位置,放在方括号内开始处表示非方括号内的任一字符;大括号表示前面的字符或表达式的重复次数;| 表示可选项,即 | 前后的表达式任选一个。
      
      3.  如果要在正则表达式中表示元字符本身,比如我就要在文本中查找问号‘?’, 那么就要使用引用符号(或称换码符号),一般是反斜杠 \'\\'。需要注意的是,在R语言中得用两个反斜杠即 ‘\\’,如要匹配括号就要写成 ’\\(\\)‘
      
      4.  不同语言或应用程序(事实上很多规则都通用)定义了一些特殊的元字符用于表示某类字符,如 \d 表示数字0-9, \D 表示非数字,\s 表示空白字符(包括空格、制表符、换行符等),\S 表示非空白字符,\w 表示字(字母和数字),\W 表示非字,\< 和 \> 分别表示以空白字符开始和结束的文本。
      
      5.  正则表达式符号运算顺序:圆括号括起来的表达式最优先,然后是表示重复次数的操作(即:* + {} ),接下来是连接运算(其实就是几个字符放在一起,如abc),最后是表示可选项的运算(|)。所以 \'foot|bar\' 可以匹配’foot‘或者’bar‘,但是 \'foot|ba{2}r\'匹配的是’foot‘或者’baar‘。
    
  • 关于正则表达式

      正则表达式是编程语言的特色,也是一大难点,几乎各类编程语言都有数据自己的正则表达式,但方法都大同小异,R里面有关正则表达式里面选择采用具有特色的perl语言风格的正则表达式。掌握好这些正则表达式的使用方法,就可以轻易地完成字符串处理任务。正则表达式,又称正规表示法、常规表示法,它使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。下面列出正则表达式常用模式:

      [0-9] 匹配所有数字字符
      [^0-9] 匹配所有非数字字符 
      [^a-z] 匹配所有非小写字母字符 
      ^ 匹配字符开头的字符
      
      $   匹配字符结尾的字符 
      \d  匹配一个数字的字符
      \d+ 匹配多个数字字符串
      \D  非数字,其他同 \d 
      \w  英文字母或数字的字符串,和 [a-zA-Z0-9] 语法一样
      \W  非英文字母或数字的字符串
      \s  空格
      \S  非空格
      \b  匹配以英文字母,数字为边界的字符串 
      \B  匹配不以英文字母,数值为边界的字符串
      
      .  匹配除换行符以外的所有单个字符 
      […] 字符组 匹配单个列出的字符
      x? 匹配 0 次或一次 x 字符串
      x* 匹配 0 次或多次 x 字符串,但匹配可能的最少次数 
      x+ 匹配 1 次或多次 x 字符串,但匹配可能的最少次数 
      .* 匹配 0 次或一次的任何字符 
      .+ 匹配 1 次或多次的任何字符 
      {m} 匹配刚好是 m 个 的指定字符串 
      {m,n} 匹配在 m个 以上 n个 以下 的指定字符串 
      {m,} 匹配 m个 以上 的指定字符串 
      [] 匹配符合 [] 内的字符 
      [^] 匹配不符合 [] 内的字符
    
  • 常用表达式语法结构

      grep, grepl, regexpr, gregexpr 函数在字符串向量中寻找特定的匹配模式pattern,具体区别在参数的选择。sub, gsub 分别用于替换单个或者全部的匹配模式,这里g意味着global。

      详细语法:
    
            grep(pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE,fixed = FALSE, useBytes = FALSE, invert = FALSE)
            
            grepl(pattern, x, ignore.case = FALSE, perl = FALSE,fixed = FALSE, useBytes = FALSE)
            
            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)
            
            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)
    
      参数说明:
      
            pattern : 用于匹配的正则表达式。只接受一个元素。
            x, text : 被匹配的字符串(向量)。
            ignore.case : 是否大小写敏感,默认为FALSE
            perl : 是否兼容Perl语言的正则表达式,默认FALSE
            value : 是否返回匹配的值。默认FALSE,那么将会返回向量的索引indice; 如果为TRUE,则返回被匹配的字符串
            fixed : 如果TRUE,则匹配整个元素。默认FALSE
            useBytes : 是否使用byte-by-byte还是character-by-character,默认FALSE
            invert : 是否取反,如果TRUE,则返回未匹配的索引indices或值values
            replacement : 适用于sub和gsub。只接受一个元素。
            grep(value = FALSE) 返回匹配向量的索引(若invert = TRUE,情况相反)
            grep(value = TRUE) 返回匹配向量的原始值
            grepl 返回布尔向量,包含是否匹配的信息
            sub 和 gsub 返回和原先向量同样长度的新向量.
            regexpr 返回一个和初始向量text长度保持一致的向量数组,元素为第一次匹配的起点位置(如果没有匹配成功则显示-1)同时还附有匹配长度(匹配显示匹配长度,否则显示-1),如果想计算bytes的长度,请使用 useBytes = TRUE
            gregexpr和regexec 均返回一个list列表,经测试内容和regexpr保持一致,但不知其具体区别,望告知
    
  • Example_1

      假设我们有一个字符向量,包括了三个字符串。我们的目标是从中抽取电邮地址。R语言中很多字符函数都能识别正则表达式,而最重要的函数就是gregexpr()。该函数的第一个参数是正则表达式,前后需要用引号,对元字符进行转义时要用\。第二个参数是等待处理的文本。那么用如下三行代码,我们从word字符向量中得到一个列表,其中第一项元素中的5表示电邮地址从第5个字符位置开始,24表示电邮地址长度为24。

      > word <- c(\'abc noboby@stat.berkeley.edu\',\'text with no email\',\'first me@mything.com also you@yourspace.com\')
      > pattern <- \'[-A-Za-z0-9_.%]+@[-A-Za-z0-9_.%]+\\.[A-Za-z]+\'
      > pattern
      [1] "[-A-Za-z0-9_.%]+@[-A-Za-z0-9_.%]+\\.[A-Za-z]+"
      > (gregout <- gregexpr(pattern,word))
      [[1]]
      [1] 5
      attr(,"match.length")
      [1] 24
      attr(,"useBytes")
      [1] TRUE
      
      [[2]]
      [1] -1
      attr(,"match.length")
      [1] -1
      attr(,"useBytes")
      [1] TRUE
      
      [[3]]
      [1]  7 27
      attr(,"match.length")
      [1] 14 17
      attr(,"useBytes")
      [1] TRUE
      
      > 
    

      下一步我们需要将电邮地址抽取出来,此时配合substr函数,即可根据需要字符串的位置来提取子集。

      > substr(word[1],gregout[[1]],gregout[[1]]+attr(gregout[[1]],\'match.length\')-1)
      [1] "noboby@stat.berkeley.edu"
      > 
    

      更方便的使用方式是根据上述方法建立一个自定义函数getcontent,参数s表示待处理的文本,参数g表示的是通过gregexpr函数处理后的结果。这个函数我们在后面还会用到。

      > getcontent <- function(s,g){
      +   substring(s,g,g+attr(g,\'match.length\')-1)
      + }
      > getcontent(word[1],gregout[[1]]) 
      [1] "noboby@stat.berkeley.edu"
      > getcontent(word[2],gregout[[2]]) 
      [1] ""
      > getcontent(word[3],gregout[[3]])
      [1] "me@mything.com"    "you@yourspace.com"
      > 
    
  • Example_2

      regexpr、gregexpr和regexec 这三个函数返回的结果包含了匹配的具体位置和字符串长度信息,可以用于字符串的提取操作。用函数获得位置信息后再进行字符串提取的操作可以自己试试看。

      > text <- c("Hellow, Adam!", "Hi, Adam!", "How are you, Adam.") 
      > regexpr("Adam", text) 
      [1]  9  5 14 
      attr(,"match.length") 
      [1] 4 4 4 
      attr(,"useBytes") 
      [1] TRUE 
      > gregexpr("Adam", text) 
      [[1]] 
      [1] 9 
      attr(,"match.length") 
      [1] 4 
      attr(,"useBytes") 
      [1] TRUE 
      [[2]] 
      [1] 5 
      attr(,"match.length") 
      [1] 4 
      attr(,"useBytes") 
      [1] TRUE 
      [[3]] 
      [1] 14 
      attr(,"match.length") 
      [1] 4 
      attr(,"useBytes") 
      [1] TRUE 
      > regexec("Adam", text) 
      [[1]] 
      [1] 9 
      attr(,"match.length") 
      [1] 4 
      [[2]] 
      [1] 5 
      attr(,"match.length") 
      [1] 4 
      [[3]] 
      [1] 14 
      attr(,"match.length") 
      [1] 4 
    
  • Example_3

      这里出现的replacement参数,在x中搜索pattern,并以文本replacement将其替换;其他的各个参数和grep的作用相同。

      > p="Who wins the prize?"
      > sub("Who",replacement="James",sub("[\\?]","!",p,perl=T))
      [1] "James wins the prize!"
    

      gsub和sub函数的不同之处在于sub函数只替换其匹配文本中第一次出现的匹配,而gsub为globe sub全局匹配替换,即替换匹配到的所有匹配值。

      > txt <- "a test of capitalizing"
      > gsub("(\\w)(\\w*)", "\\U\\1\\L\\2", txt, perl=TRUE)
      [1] "A Test Of Capitalizing"
      
      > gsub("\\b(\\w)", "\\U\\1", txt, perl = T)
      [1] "A Test Of Capitalizing"
    

      perl正则表达式中,\为转义符,\w为元字符,匹配数字型的字符;* 为匹配0个或多个x,\U:大写字符,直到字符串末尾或碰到\E,\L:小写字符,直到字符串末尾或碰到\E,\b:匹配以英文字母,数字为边界的字符串, \1,\2分别为匹配第一组括号和第二组括号。使用以上的gsub替换,可以将文本每个单词的首字母大写。

      regexpr,gregexpr,regexec,它们可以返回和text相同长度的向量,包括不匹配的值

      > m=c("e","the","end","of","line")
      > regexpr("e",m)
      [1]  1  3  1 -1  4
    
      attr(,"match.length")
      [1]  1  1  1 -1  1
      
      attr(,"useBytes")
      [1] TRUE
    
  • Example_4

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

      #### grep 和 grepl
      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
      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"
    

      除了上面的字符串的查询,有时还会用到完全匹配,这是会用到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
    

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

  • Example_5

      正则匹配是一个非常常用的字符搜索手段,在数据挖掘中起着非常重要的作用。所以虽然它是一种常规手段,但我还是另起一段来专门讲述这个概念。

      在R当中,可以使用三种正则:
      
      扩展正则
      基本正则
      Perl风格正则
      
      正则的使用主要涉汲以下7个函数:grep, grepl, sub, gsub, regexpr, gregrexpr, regexec。而象strsplit, apropos以及browseEnv都是基于这7个函数基础之上的。
      我们先从正则讲起。
    

      假设我们现在需要从一堆字符当中找到一个符合一定规则的字符串,比如说从一个表格中找到所有人的email地址,或者说找到一段文字中所有的URL地址,你会如何做呢?嗯,回答肯定是正则了。正则就是做这个用的。我们知道一个email地址通常都是这样的(最简单情行),xxxxxx@ppp.ddd,其中,xxxxxx可能是任意字母,数字,以及下划线,点等组成,而ppp.ddd就是一个域名地址。它们之间以@相隔。在正则下是这样表示,[1]+@[A-Za-z0-9\.-]+\.[A-Za-z]{2,4}$

      > pattern="^[A-Za-z0-9\\._%+-]+@[A-Za-z0-9\\.-]+\\.[A-Za-z]{2,4}$"
      > str<-c("abc","someone@qiuworld.com","efg","anotherone@gmail.com","thirdone@yahoo.cn")
      > #grepl会返回一个逻辑值,l就代表logical, g就代表global
      > grepl(pattern,str)
      [1] FALSE  TRUE FALSE  TRUE  TRUE
      > #grep会返回匹配的id
      > grep(pattern,str)
      [1] 2 4 5
      > #regexpr会返回一个数字,1表示匹配,-1表示不匹配,还会返回两个属性,匹配的长度以及是否使用useBytes。useBytes一般很少会使用到false,因为我们不处理宽字符。
      > regexpr(pattern,str)
      [1] -1  1 -1  1  1
      attr(,"match.length")
      [1] -1 20 -1 20 17
      attr(,"useBytes")
      [1] TRUE
    
    
      > #regexec会返回一个list,下面的内容是第一个匹配及其长度
      > regexec("\\w+@\\w+\\.[a-zA-Z]{2,4}","abc@qiuworld.com,efd@qiuworld.com")
      [[1]]
      [1] 1
      attr(,"match.length")
      [1] 16
       
      > #gregexpr也会返回一个list, 下面的内容是每一个匹配及其长度以及useBytes。g就代表global
      > gregexpr("\\w+@\\w+\\.[a-zA-Z]{2,4}","abc@qiuworld.com,efd@qiuworld.com")
      [[1]]
      [1]  1 18
      attr(,"match.length")
      [1] 16 16
      attr(,"useBytes")
      [1] TRUE
      
      > #sub和gsub都用来做正则替换,其区别只在于g所代表的global。sub只替换遇到的第一个匹配,而gsub会替换所有的匹配。
      > #需要注意的是,这里的匹配都是对应的一个字符串而言的,如果是多个字符串,要区别为每一个来单独对待。
      > sub("\\w+@\\w+\\.[a-zA-Z]{2,4}","sub_function","abc@qiuworld.com,efd@qiuworld.com")
      [1] "sub_function,efd@qiuworld.com"
      > gsub("\\w+@\\w+\\.[a-zA-Z]{2,4}","gsub_function","abc@qiuworld.com,efd@qiuworld.com")
      [1] "gsub_function,gsub_function"
      </c>
      
      
      正则的使用我们已经看到了,但是让人看不明白就是字符. \ | ( ) [ { ^ $ * + - ? 这些符号都是什么意思啊?
      下面我就先来仔细讲讲perl中的正则符号
      <pre >
      .     # 除了换行以外的任意字符
      ^     # 一行字符串的起始,它并不代表第一个字符,只代表这里开始新的一行字符串。
      $     # 一行字符串的结束,它并不代表最后一个字符(因为换行符并不会被包含进匹配当中),只代表一行字符串到这里结束。
      *     # 零个或者多个之前的字符
      +     # 一个或者多个之前的字符
      ?     # 零个或者一个之前的字符
      
      # 示例部分
      t.e           # t后面跟任意一个非换行字符然后跟字符e
                    # 它可以匹配        the
                    #                 tre
                    #                 tle
                    # 但是不匹配 te
                    #           tale
      ^f            # 一行字符以f起始
      ^ftp  # 一行字符以ftp起始
      e$            # 一行字符以e结尾
      tle$  # 一行字符以tle结尾
      und*  # un跟着零个或者多个d字符
                    # 它会匹配 un
                    #         und
                    #         undd
                    #         unddd (etc)
      .*            # 任意一个无换行的字符串,
                    #  . 匹配任何一个非换行字符
                    #  * 将匹配一个或者多个之前的字符.
      ^$            # 一个空行.
       
      # 在正则中有方括号[],代表可以匹配其中任何一个字符。而^在[]中就有了新的意义,代表“非”, -代表了“之间”
      [qjk]         # q,j,k中任意一个字符
      [^qjk]                # 非q,j,k的任意其它字符
      [a-z]         # a至z中任意一个小写字符
      [^a-z]                # 非任意一个a至z小写字符的其它字符(可以是大写字符)
      [a-zA-Z]      # 任意一个英文字母
      [a-z]+                # 一个或者多个小写英文字母
       
      # |代表或者 小括号(...)可以把或者的部分括起来。注意小括号可能还有别的用途,但是在R当中先不使用。
       
      jelly|cream   # jelly或者cream
      (eg|le)gs     # eggs或者legs
      (da)+         # da或者dada或者dadada 或者更多个da的重复
       
      # 大括号括住1至2个数字,代表重复次数。
      *     # 零个或者多个之前的字符
      +     # 一个或者多个之前的字符
      ?     # 零个或者一个之前的字符
      {n}   # n个之前的字符
      {n,}  # 大于等于n个之前的字符
      {n,m} # n至m个之前的字符
       
      # 下面的是一些字符被转义符\转义会赋以了一些新的(有可能是约定俗成的)意义
      \n            # 换行符
      \t            # tab
      \w            # 任意字母(包括下划线)或者数字
                    # 等同于[a-zA-Z0-9_]
      \W            # \w的反义.
                    # 等同于[^a-zA-Z0-9_]
      \d            # 任意一个数字,等同于[0-9]
      \D            # \d的反义,等同于[^0-9]
      \s            # 任意一个空格,比如,
                    # space, tab, newline, 等
      \S            # \s的反义,任意一个非空格
      \b            # 单词的边界, 不能使用在[]内
      \B            # \b的反义
       
      # 很明显,对于保留字符$, |, [, ), \, / 等等都需要转义字符\来转义表示:
       
      \|            # 竖线
      \[            # \[左方括号 \]右方括号
      \)            # \(左小括号 \)右小括号
      \*            # 星号
      \^            # 开号
      \/            # 反斜杠
      \\            # 斜杠
    

      接下来再讲一下POSIX中定义的一些特殊意义的字符(R中预定义的字符组)

      [:alnum:]  # [:alpha:]和[:digit:]的组合
      [:alpha:]     # [:lower:]和[:upper:]的组合
      [:blank:]     # 空格(space, tab),不包括换行符
      [:cntrl:]     # 控制符,在ASCII码中000~037或者177
      [:digit:]     # 任意数字:0-9
      [:graph:]     # [:alnum:]和[:punct:]的组合
      [:lower:]     # 当前字符集的小写字母(小写字母:a-z)
      [:print:]     # 可打印出来的字符,[:graph:]以及空格(即:[:alnum:],[:punct:]和[:space:])
      [:punct:]     # 标点符号,包括:^ ! " # $ % & \' ( ) * + - . / : ; < = > ? @ [ ] \ _ { } ` ~
      [:space:]     # 空格,包括tab, newline, vertical tab, form feed, carriage return, and space
      [:upper:]     # 当前字符集的大写字母(大写字母:A-Z)
      [:xdigit:]    # 16进制数 0-9a-fA-F
    

      代表字符组的特殊符号

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

      还有两个锚点特殊字符

      ^  # 一行字符串的起始,它并不代表第一个字符,只代表这里开始新的一行字符串。
      $     # 一行字符串的结束,它并不代表最后一个字符(因为换行符并不会被包含进匹配当中),只代表一行字符串到这里结束。
      \< # 单词左边界
      \> # 单词右边界
    

      弄清楚了这些正则符号,我们再回过头来看一点之前的

      pattern <- "^[A-Za-z0-9\\._%+-]+@[A-Za-z0-9\\.-]+\\.[A-Za-z]{2,4}$"
      可以改写为
      pattern <- "^[\\w\\._%+-]+@[\\w\\.-]+\\.[A-Za-z]{2,4}$"
      或者
      pattern <- "^[[:alnum:]\\._%+-]+@[[:alnum:]\\.-]+\\.[[:alpha:]]{2,4}$"
    

      有人会问了,为什么转义字符都要写两次啊?因为R本身也把 \ 当成转义字符,所以在写pattern的时候,就需要使用\\ 来表示转义字符。还有一种办法就是设置fixed为TRUE。那么参数中perl是什么意思呢?其实就是指是否使用PCRE的算法,我们来看实例:

      > regexpr("foo|foobar","myfoobar")
      [1] 3
      attr(,"match.length")
      [1] 6
      attr(,"useBytes")
      [1] TRUE
      > regexpr("foo|foobar","myfoobar", perl=TRUE)
      [1] 3
      attr(,"match.length")
      [1] 3
      attr(,"useBytes")
      [1] TRUE
    

参考资料


  1. A-Za-z0-9\._%+- ↩︎