添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • 52.2.1 evalCpp() 转换单一计算表达式
  • 52.2.2 cppFunction() 转换简单的C++函数—Fibnacci例子
  • 52.2.3 sourceCpp() 转换C++程序—正负交替迭代例子
  • 52.2.4 sourceCpp() 转换C++源文件中的程序—正负交替迭代例子
  • 52.2.5 sourceCpp() 转换C++源程序文件—卷积例子
  • 52.2.6 随机数例子
  • 52.2.7 bootstrap例子
  • 52.2.8 在Rmd文件中使用C++源程序文件
  • 53 R与C++的类型转换
  • 53.1 wrap() 把C++变量返回到R中
  • 53.2 as() 函数把R变量转换为C++类型
  • 53.3 as() wrap() 的隐含调用
  • 54 Rcpp 属性
  • 54.1 Rcpp属性介绍
  • 54.2 在C++源程序中指定要导出的C++函数
  • 54.2.1 特殊注释 //[[Rcpp::export]]
  • 54.2.2 修改导出的函数名
  • 54.2.3 可导出的函数
  • 54.3 在R中编译链接C++代码
  • 54.3.1 sourceCpp() 函数中直接包含C++源程序字符串
  • 54.3.2 cppFunction() 函数中直接包含C++函数源程序字符串
  • 54.3.3 evalCpp() 函数中直接包含C++源程序表达式字符串
  • 54.3.4 depends 指定要链接的库
  • 54.4 Rcpp属性的其它功能
  • 54.4.1 自变量有缺省值的函数
  • 54.4.2 异常传递
  • 54.4.3 允许用户中断
  • 54.4.4 把R代码写在C++源文件中
  • 54.4.5 invisible 要求函数结果不自动显示
  • 54.4.6 在C++中调用R的随机数发生器
  • 55 Rcpp提供的C++数据类型
  • 55.1 RObject类
  • 55.2 IntegerVector类
  • 55.2.1 IntegerVector示例1:返回完全数
  • 55.2.2 IntegerVector示例2:输入整数向量
  • 55.3 NumericVector类
  • 55.3.1 示例1:计算元素 \(p\) 次方的和
  • 55.3.2 示例2: clone 函数
  • 55.3.3 示例3:向量子集
  • 55.4 NumericMatrix类
  • 55.4.1 示例1:计算矩阵各列模的最大值
  • 55.4.2 示例2:把输入矩阵制作副本计算元素平方根
  • 55.4.3 示例3:访问列子集
  • 55.5 Rcpp的其它向量类
  • 55.5.1 Rcpp的LogicalVector类
  • 55.5.2 Rcpp的CharacterVector类型
  • 55.6 Rcpp提供的其它数据类型
  • 55.6.1 Named类型
  • 55.6.2 List类型
  • 55.6.3 Rcpp的DataFrame类
  • 55.6.4 Rcpp的Function类
  • 55.6.5 Rcpp的Environment类
  • 56 Rcpp糖
  • 56.1 简单示例
  • 56.2 向量化的运算符
  • 56.2.1 向量化的四则运算
  • 56.2.2 向量化的赋值运算
  • 56.2.3 向量化的二元逻辑运算
  • 56.2.4 向量化的一元运算符
  • 56.3 用Rcpp访问数学函数
  • 56.4 用Rcpp访问统计分布类函数
  • 56.5 在Rcpp中产生随机数
  • 56.6 返回单一逻辑值的函数
  • 56.7 返回糖表达式的函数
  • 56.7.1 is_na
  • 56.7.2 seq_along
  • 56.7.3 seq_len
  • 56.7.4 pmin pmax
  • 56.7.5 ifelse
  • 56.7.6 sapply lapply
  • 56.7.7 sign
  • 56.7.8 diff
  • 56.8 R与Rcpp不同语法示例
  • 56.9 用RcppArmadillo执行矩阵运算
  • 56.9.1 生成多元正态分布随机数
  • 56.9.2 快速计算线性回归
  • 57 用Rcpp帮助制作R扩展包
  • 57.1 不用扩展包共享C++代码的方法
  • 57.2 生成扩展包
  • 57.2.1 利用已有基于Rcpp属性的源程序制作扩展包
  • 57.2.2 DESCRIPTION文件
  • 57.2.3 NAMESPACE文件
  • 57.3 重新编译
  • 57.4 建立C++用的接口界面
  • XI 其它
  • 58 R编程例子
  • 58.1 R语言
  • 58.1.1 用向量作逆变换
  • 58.1.2 斐波那契数列计算
  • 58.1.3 穷举所有排列
  • 58.1.4 可重复分组方式穷举
  • 58.1.5 升降连计数
  • 58.1.6 高斯八皇后问题
  • 58.1.7 最小能量路径
  • 58.2 概率
  • 58.2.1 智者千虑必有一失
  • 58.2.2 圆桌夫妇座位问题
  • 58.3 科学计算
  • 58.3.1 城市间最短路径
  • 58.3.2 Daubechies小波函数计算
  • 58.3.3 房间加热温度变化
  • 58.4 统计计算
  • 58.4.1 线性回归实例
  • 58.4.2 核回归与核密度估计
  • 58.4.3 二维随机模拟积分
  • 58.4.4 潜周期估计
  • 58.4.5 ARMA(1,1)模型估计
  • 58.4.6 VAR模型平稳性
  • 58.4.7 贮存可靠性评估
  • 58.5 数据处理
  • 58.5.1 小题分题型分数汇总
  • 58.5.2 类别编号重排
  • 58.6 文本处理
  • 58.6.1 用R语言下载处理《红楼梦》htm文件
  • 59 使用经验
  • 59.1 文件管理
  • 59.1.1 工作空间
  • 59.2 程序格式
  • A R Markdown文件格式
  • A.1 R Markdown文件
  • A.2 R Markdown文件的编译
  • A.2.1 编译的实际过程
  • A.3 在R Markdown文件中插入R代码
  • A.4 输出表格
  • A.5 利用R程序插图
  • A.6 冗余输出控制
  • A.7 代码段选项
  • A.7.1 代码和文本输出结果格式
  • A.7.2 图形选项
  • A.7.3 缓存(cache)选项
  • A.8 章节目录链接问题
  • A.9 其它编程语言引擎
  • A.10 交互内容
  • A.11 属性设置
  • A.11.1 YAML元数据
  • A.11.2 输出格式
  • A.11.3 输出格式设置
  • A.11.4 目录设置
  • A.11.5 章节自动编号
  • A.11.6 Word输出章节自动编号及模板功能
  • A.11.7 HTML特有输出格式设置
  • A.11.8 关于数学公式支持的设置
  • A.11.9 输出设置文件
  • A.12 LaTeX和PDF输出
  • A.12.1 TinyTex的安装使用
  • A.12.2 Rmd中Latex设置
  • A.13 生成期刊文章
  • A.14 附录:经验与问题
  • A.14.1 Word模板制作
  • A.14.2 数学公式设置补充
  • B 用bookdown制作图书
  • B.1 介绍
  • B.2 一本书的设置
  • B.3 章节结构
  • B.4 书的编译
  • B.5 交叉引用
  • B.6 数学公式和公式编号
  • B.7 定理类编号
  • B.8 文献引用
  • B.9 插图
  • B.10 表格
  • B.10.1 Markdown表格
  • B.10.2 kable() 函数制作表格
  • B.10.3 R中其它制作表格的包
  • B.11 数学公式的设置
  • B.12 使用经验
  • B.12.1 学位论文
  • B.12.2 LaTeX
  • B.12.3 算法
  • B.12.4 中文乱码
  • B.12.5 图片格式
  • B.12.6 其它经验
  • B.13 bookdown的一些使用问题
  • C 用R Markdown制作简易网站
  • C.1 介绍
  • C.2 简易网站制作
  • C.2.1 网站结构
  • C.2.2 编译
  • C.2.3 内容文件
  • C.2.4 网站设置
  • C.3 用blogdown制作网站
  • C.3.1 生成新网站的框架
  • C.3.2 网页内容文件及其设置
  • C.3.3 初学者的工作流程
  • C.3.4 网站设置文件
  • C.3.5 静态文件
  • D 制作幻灯片
  • D.1 介绍
  • D.2 Slidy幻灯片
  • D.2.1 文件格式
  • D.2.2 幻灯片编译
  • D.2.3 播放控制
  • D.2.4 生成单页HTML
  • D.2.5 数学公式处理与输出设置文件
  • D.2.6 其它选项
  • D.2.7 slidy幻灯片激光笔失效问题的修改
  • D.3 MS PowerPoint幻灯片
  • D.4 Bearmer幻灯片格式
  • D.5 R Presentation格式
  • References
  • 编著:李东风
  • 正则表达式(regular expressions)用于表示各种复杂模式。 基本R中的正则表达式规则可以用POSIX 1003.2标准或者Perl规则。 建议使用perl语言的正则表达式, 在基本R的有关函数中规定参数 perl=TRUE

    stringr包提供了更方便的正则表达式功能, 其正则表达式规则是ICU正则表达式规则, 针对UTF-8编码的文本数据, 基本与perl规则兼容。

    在正则表达式的模式(pattern)中,

    .*+?{}\[]^$()

    等字符是特殊字符,有特殊的解释。 除了 \ 之外的其它12个都称为“ 元字符 ”(meta characters)。

    在R语言中使用正则表达式时, 需要注意R字符型常量中一个 \ 要写成两个。 为了避免 \ 的转义, 可以使用R的原始字符串功能, 将模式写成 r"{...}" 这样的形式, 其中 ... 是实际的正则表达式内容, 不需要将 \ 写成两个。

    49.1.1 字面匹配与匹配显示

    如果模式中不含特殊字符,匹配为原样的子串。 也叫做字面(literal)匹配。 stringr包提供了定义正则表达式、匹配正则表达式、按正则表达式替换、抽取匹配结果、用富文本显示匹配结果等强大功能, 其中 str_view() 函数可以在RStudio软件中高亮显示匹配部分, 并在匹配部分两边用 <> 界定。 在所有输出中都用 <> 界定匹配部分。

    下面的程序在字符型向量 x 的三个字符串元素中查找子字符串 "the" 并加亮显示(一般输出中用 <> 界定匹配部分):

    x <- c("New theme", "Old times", "In the present theme")
    str_view(x, "the")
    ## [1] │ New <the>me
    ## [3] │ In <the> present <the>me

    因为某些希望原样匹配的字符串中可能有元字符(对正则表达式需要特殊解释的字符), 所以字面匹配的模式应该用 fixed() 函数保护。 在文件名中找到以 .txt 结尾的:

    ## [2] │ data<.txt>

    49.1.2 不区分大小写匹配和 regex 函数

    str_view(string, pattern) 中的 pattern 应该为正则表达式类型, 如果输入了字符串, 会自动被函数 regex() 转换成正则表达式类型。 正则表达式的模式一般是区分大小写的,

    str_view(c("Dr. Wang", "DR. WANG", "dR. W.R."), "Dr")
    ## [1] │ <Dr>. Wang

    通过在 regex() 函数中加选项 ignore_case=TRUE 可以进行不区分大小写的匹配:

    ## [1] │ <Dr>. Wang
    ## [2] │ <DR>. WANG
    ## [3] │ <dR>. W.R.

    fixed() 函数也允许使用 ignore_case=TRUE ,如:

    ## [1] │ <Dr>. Wang
    ## [2] │ <DR>. WANG
    ## [3] │ <dR>. W.R.

    在模式前面附加 (?i) 前缀式选项也可以实现不区分大小写匹配:

    ## [1] │ <Dr>. Wang
    ## [2] │ <DR>. WANG
    ## [3] │ <dR>. W.R.

    也可以提前将输入的源字符串统一转换为小写, 这时模式中也仅使用小写, 也可以达到不区分大小写匹配的目的。 还可以将模式中大写字母和小写字母用字符类表示(见§ 49.1.4 )。

    49.1.3 用句点匹配单个字符

    在模式中用“.”匹配任意一个字符(除了换行符 "\n" ,能否匹配此字符与选项有关)。

    s <- c("abc", "cabs", "lab")
    str_view(s, "ab.")
    ## [1] │ <abc>
    ## [2] │ c<abs>

    像句点这样的字符称为 元字符 (meta characters), 在正则表达式中有特殊作用。 如果需要匹配句点本身,用“ [.] ”或者“ \. ”表示。 比如,要匹配 a.txt 这个文件名,如下做法有错误:

    ## [1] │ <a.txt>
    ## [2] │ <a0txt>

    结果连 a0txt 也匹配了。用“[.]”表示句点则将句点不做特殊解释:

    ## [1] │ <a.txt>
    ## [1] │ <a.txt>

    注意在R语言字符型常量中一个 \ 需要写成两个。

    如果仅需按照原样进行查找, 也可以将 pattern 的字符串用 fixed() 函数保护,如:

    str_view(c("a.txt", "a0txt"), fixed("a.txt"))
    ## [1] │ <a.txt>

    49.1.4 匹配一组字符中的某一个

    模式中使用方括号给定一个字符类, 单个字符与字符类中任何一个字符相同都算是匹配成功。 比如,模式“ [ns]a.[.]xls ” 表示匹配的第一个字符是 n s , 第二个字符是 a ,第三个字符任意,第四个字符是句点, 然后是 xls

    ## [1] │ <sa1.xls>
    ## [2] │ d<na2.xls>s
    ## [3] │ <nat.xls>

    注意匹配并不需要从开头匹配到结尾, 中间匹配是允许的,类似于搜索符合某种规律的子串。 在上例中第二个元素是从第二个字符开始匹配的,也没有匹配到末尾。

    例:模式 [Rr]eg[Ee]x 可以匹配 RegEx Regex regex regEx

    在“ [] ”中有一些特殊字符:

  • 允许用 - 表示一个范围, 如 [a-z] 匹配小写英文字母, [A-Z] 匹配大写英文字母, [a-zA-Z] 匹配大小写的英文字母, [a-zA-Z0-9] 匹配大小写的英文字母和数字。 为了匹配一个16进制数字, 可以用 [0-9A-Fa-f]
  • 在开头用“ ^ ”表示余集,比如 "[^0-9]" 表示匹配非数字。
  • 如果要在 [] 包含原样的 ^ - ,可以用前导转义符 \
  • 例:下面的模式“ [ns]a[0-9][.]xls ”要求匹配的第三个字符为数字。

    str_view(c("sa1.xls", "dna2.xlss", "nat.xls"), 
      "[ns]a[0-9][.]xls")
    ## [1] │ <sa1.xls>
    ## [2] │ d<na2.xls>s

    例:模式 [ns]a[^0-9][.]xls 要求匹配的第三个字符不能为数字:

    str_view(c("sa1.xls", "dna2.xlss", "nat.xls"), 
      "[ns]a[^0-9][.]xls")
    ## [3] │ <nat.xls>

    要求第三个字符为 ^ - 的例子:

    ## [2] │ <5 - 3>
    ## [3] │ <5 ^ 3>

    49.1.5 原样匹配元字符

    元字符(meta characters)是在正则表达式中有特殊含义的字符。 比如句点可以匹配任意一个字符, 左方括号代表字符集合的开始。 所以元字符不能直接匹配自身, 将元字符写在方括号内可以原样匹配这些字符, 比如可以用“ [.] ”匹配一个句点, 用 [$] 匹配“ $ ”符号,用 [|] 匹配“ | ”符号,

    还可以用前导转义字符“ \ ”来表示某个元字符需要原样匹配, 为匹配左方括号,在前面加上转义字符 \ 变成 \[ , 但是在R字符串中一个 \ 必须用 \\ 表示, 所以模式“ \[ ”在R中写成字符串常量, 必须写成 "\\[" 。 其它的元字符如果要原样匹配也可以在前面加上转义字符 \ , 比如匹配 \ 本身可以用 \\ ,但是在R字符型常量中需要写成 "\\\\" 。 用R的原始字符串格式可以不用将 \ 写成 \\ , 原始字符串是指字符串前面加上 r 字母作为前缀, 用原始字符串作为模式字符串时, 模式需要用括号包裹起来。

    例,匹配 x[5] ,因为 [ 是元字符,需要写成:

    str_view(c("int x;", "int x[5]"), "int x\\[5\\]")
    ## [2] │ <int x[5]>

    可以用原始字符串格式写成:

    ## [2] │ <int x[5]>

    当然,因为模式中所有字符都不想作为特殊字符, 也可以用 fixed() 说明:

    str_view(c("int x;", "int x[5]"), fixed("int x[5]"))
    ## [2] │ <int x[5]>

    注意其中的方括号不需要用反斜杠保护。

    49.1.6 匹配某类字符

    49.1.6.1 匹配空白

    表示空白的元字符有:

    \f 换页符
    \n 换行符
    \r 回车符
    \t 制表符
    \v 垂直制表符

    不同操作系统的文本文件的行分隔符不同, 为了匹配Windows格式的文本文件中的空行, 用“ \r\n\r\n ”; 为了匹配Unix格式的文本文件中的空行则用“ \r\r ”。 写成R的字符型常量时, 这些表示本身也是R的相应字符的表示, 所以在R字符型常量中这些字符不需要用两个 \ 表示一个 \

    匹配任意一个空白字符用“ \s ”, 这等价于“ [ \f\n\r\t\v] ”, 但是其中的 \ 在R字符串中要写成 \\ , 所以 \s 写成 \\s 。 大写的“ \S ”则匹配任意一个非空白的字符。

    49.1.6.2 匹配数字

    \d 匹配一个数字,相当于 [0-9] 。 用 \D 匹配一个非数字。

    str_view(c("n1.xls", "na.xls"), r"{n\d[.]xls}")
    ## [1] │ <n1.xls>

    49.1.6.3 匹配字母、数字、下划线

    匹配字母、数字、下划线字符用 \w (小写), 等价于 [a-zA-Z0-9_] \W (大写)匹配这些字符以外的字符。

    ## [1] │ file-<s1.>xls

    可以看出,模式匹配了 s1. 而没有匹配 s#.

    49.1.6.4 十六进制和八进制数

    在模式中可以用十六进制数和八进制数表示特殊的字符。 十六进制数用 \X 引入, 比如 \X0A 对应 \n 字符。 八进制数用 \0 引入, 比如 \011 表示 \t 字符。

    例如, str_view("abc\nefg\n", r"{\x0A}") 可以匹配两个换行符。

    49.1.6.5 POSIX字符类

    \d , \w 这样的字符类不方便用在方括号中组成字符集合, 而且也不容易记忆和认读, 按照R的双反斜杠规则写在字符串中就更难认读。

    在模式中方括号内可以用 [:alpha:] 表示任意一个字母。 比如, [[:alpha:]] 匹配任意一个字母(外层的方括号表示字符集合, 内层的方括号是POSIX字符类的固有界定符)。

    这样的POSIX字符类有:

  • [:alpha:] 表示任意一个字母;
  • [:lower:] 为小写字母;
  • [:upper:] 为大写字母;
  • [:digit:] 为数字;
  • [:xdigit:] 为十六进制数字。
  • [:alnum:] 为字母数字(不包括下划线);
  • [:blank:] 为空格或制表符;
  • [:space:] 为任何一种空白字符,包括空格、制表符、换页符、换行符、回车符;
  • [:print:] 为可打印字符;
  • [:graph:] [:print:] 一样但不包括空格;
  • [:punct:] [:print:] 中除 [:alnum:] 和空白以外的所有字符;
  • str_view(c("x1", "_x", ".x", ".1"), 
      "[[:alpha:]_.][[:alnum:]_.]")
    ## [1] │ <x1>
    ## [2] │ <_x>
    ## [3] │ <.x>
    ## [4] │ <.1>

    模式匹配长度为2的字符串, 第一个字符是字母、下划线或者小数点, 第二个字符是字母、数字、下划线或者小数点。 这个模式试图匹配由两个字符组成的合法R变量名, 但是最后一个非变量名 .1 也被匹配了。 解决这样的问题可以采用后面讲到的 | 备择模式。

    49.1.7 备择模式

    如果有两种模式都算正确匹配,则用 | 连接这两个模式表示两者都可以。 例如,某个人的名字用James和Jim都可以, 表示为 James|Jim , 如

    str_view(c("James, Bond", "Jim boy"), "James|Jim")
    ## [1] │ <James>, Bond
    ## [2] │ <Jim> boy

    两个字符的合法R变量名的匹配:

    ## [1] │ <x1>
    ## [2] │ <_x>
    ## [3] │ <.x>

    49.1.8 匹配开头和末尾

    模式匹配相当于在字符串内部搜索某种模式, 如果必须从字符串开头匹配, 在模式中取第一个模式规定为 ^ \A 。 如果模式中最后一个字符是 $ \Z , 则需要匹配到字符串末尾。 用 \Z 匹配字符串末尾时如果末尾有一个换行符则匹配到换行符之前。

    str_view(c(
      "n1.xls", "na.xls", "cn1.xls", "n1.xlsx"), 
      r"{^n\d[.]xls$}")
    ## [1] │ <n1.xls>

    只匹配了第一个输入字符串。

    用“ \A ”和“ \Z ”的写法:

    有时候源文本的每个字符串保存了一个文本文件内容, 各行用 \n 分隔, 后面将给出匹配每行的行首与行尾的方法。

    49.1.9 单词边界

    \b 匹配单词边界, 这样可以查找作为单词而不是单词的一部分存在的内容。 \B 匹配非单词边界。

    ## [1] │ a <cat> meaos
    找到单独的函数 sum 而不是子串 sum

    ## [4] │ <sum>(x)

    49.1.10 重复匹配

    49.1.10.1 加号重复匹配

    模式中在一个字符或字符集合后加后缀 + 表示一个或多个前一字符。

    ## [1] │ <sa1>
    ## [2] │ d<sa123>

    例如,匹配电子邮件地址:

    ## [1] │ <abc123@efg.com>

    匹配的电子邮件地址在 @ 前面可以使用任意多个字母、数字、下划线, 在 @ 后面由小数点分成两段, 每段可以使用任意多个字母、数字、下划线。 这里用了 ^ $ 表示全字符串匹配。

    49.1.10.2 星号和问号重复匹配

    在一个字符或字符集合后加后缀 * 表示零个或多个前一字符, 后缀 ? 表示零个或一个前一字符。

    ^https?://[[:alnum:]./]+$ 可以匹配http或https开始的网址。
    ## [1] │ <http://www.163.net>
    ## [2] │ <https://123.456.>

    (注意第二个字符串不是合法网址但是按这个正则表达式也能匹配)

    x[[:digit:]]* 能匹配“x”, “x1”, “x123”这样的变量名,如:

    ## [1] │ <x>
    ## [2] │ <x1>
    ## [3] │ <x123>
    ## [1] │ <x>
    ## [2] │ <x1>
    ## [3] │ <x123>

    从上面的例子可以看出, 在指定某一字符类(或集合)重复时, 不需要严格重复前一字符, 而是只要保持同一类即可。

    49.1.10.3 计数重复

    问号可以表示零个或一个, 而加号、星号重复不能控制重复次数。 在后缀大括号中写一个整数表示精确的重复次数。

    str_view(c("1", "12", "123", "1234"), 
      "[[:digit:]]{3}")
    ## [3] │ <123>
    ## [4] │ <123>4

    模式匹配的是三位的数字。 因为没有要求从开头一直匹配到末尾, 所以三位以上数字也能匹配其中开始的三位。

    可以在后缀大括号中指定重复的最小和最大次数, 中间用逗号分隔。 最小重复数允许指定为0。 重复数的逗号后面空置表示重复数没有上限。 例如,后缀 {3,} 表示前一模式必须至少重复3次。

    例:月日年的日期格式可以用

    [[:digit:]]{1,2}[-/][[:digit:]]{1,2}[-/][[:digit:]]{2,4}

    比较长的正则表达式会很难认读, 我们可以用 paste0() 函数将其拆分为几个部分, 并对每一部分添加适当的注释。 如 (注意这个模式还会匹配非日期)

    ## [1] │ <2/4/1998>
    ## [2] │ <13/15/198>

    也可以用 regex() 函数中加选项 comment=TRUE 加注释, 这时一个正则表达式可以写成多行, 在行尾添加注释, 所有原样空格必须转义或写在字符集中,如:

    ## [1] │ <2/4/1998>
    ## [2] │ <13/15/198>

    49.1.11 贪婪匹配和懒惰匹配

    无上限的重复匹配如 * , + , {3,} 等缺省是贪婪型的, 重复直到文本中能匹配的最长范围。 比如我们希望找出圆括号这样的结构, 很容易想到用 \(.+\) 这样的模式(注意圆括号是元字符,需要用反斜杠保护), 但是这不会恰好匹配一次, 模式会一直搜索到最后一个 ) 为止。

    str_view("(1st) other (2nd)", r"{\(.+\)}")
    ## [1] │ <(1st) other (2nd)>

    我们本来期望的是提取两个“(1st)”和“(2nd)”组合, 不料整个地提取了“ (1st) other (2nd) ”。 这就是因为 .+ 的贪婪匹配。

    如果要求尽可能短的匹配, 使用 *? , +? , {3,}? 等“懒惰型”重复模式。 在无上限重复标志后面加问号表示懒惰性重复。

    比如,上例中模式修改后得到了期望的结果:

    ## [1] │ <(1st)> other <(2nd)>

    懒惰匹配会造成搜索效率降低, 应仅在需要的时候使用。

    49.1.12 允许句点匹配换行

    句点通配符一般不能匹配换行,如

    str_view("(1,\n2)", r"{\(.+?\)}")

    跨行匹配失败。

    一种办法是预先用 str_replace_all() gsub() 把所有换行符替换为空格。 但是这只能解决部分问题。

    解决方法是在将模式用 regex() 保护并加选项 dotall=TRUE , 使得句点通配符可以匹配换行符, 称为句点全匹配模式。

    ## [1] │ <(1,
    ##     │ 2)>

    也可以在在Perl格式的正则表达式开头添加 (?s) 选项,

    ## [1] │ <(1,
    ##     │ 2)>

    49.1.13 多行模式

    regex() 函数中加选项 multiline=TRUE , 或者在正则表达式开头用 (?m) 表示把整个输入字符串看成用换行符分开的多行。 这时 ^ $ 匹配每行的开头和结尾, “每行”是指字符串中用换行符分开的各个字符子串。 (?s) (?m) 可以同时使用,写成 (?sm)

    str_view("(1,2)\n(3,4)\n", r"{^\(.+?\)$}")

    元数据中包含两行内容, 结果没有能够匹配, 这是因为模式要求从整个字符串开头一直匹配到末尾。

    增加 multiline=TRUE 则可以对每行分别匹配, 结果找到两处匹配:

    ## [1] │ <(1,2)>
    ##     │ <(3,4)>
    ##     │

    使用 (?m) 选项:

    ## [1] │ <(1,2)>
    ##     │ <(3,4)>
    ##     │

    49.1.14 逐行处理

    虽然正则表达式有多行和跨行选项, 但是当源数据很长时, 匹配效率会很低。

    R的 readLines() 函数可以把一整个文本文件读成一个字符型向量, 每个元素为一行, 元素中不包含换行符。 R的字符型函数可以对这样的字符型向量每个元素同时处理, 也就实现了逐行处理。

    如果字符串 x 中包含了一整个文本文件内容, 其中以 \n 分隔各行, 为了实现逐行处理, 可以先用 str_split() 函数拆分成不同行:

    cl <- strs_plit(x, "\r?\n")[[1]]

    结果将是一个字符型向量, 每个元素是原来的一行,最后一个元素是空字符串。

    x <- c("This is first line.\nThis is second line.\n")
    cl <- str_split(x, "\r?\n")[[1]]
    
    ## [1] "This is first line."  "This is second line." ""

    49.1.15 替换

    stringr包的str_replace_all(string, pattern, replacement)在字符型向量string的每个元素中查找模式pattern, 并将所有匹配按照replacement进行替换。 在replacement可以用\1, \2中表示模式中的捕获, 除此之外元字符没有特殊作用。

    基本R中gsub()有类似功能。

    ## [1] "123456" "011"
    ## [1] "456,123" "011"

    注意源数据中第二个元素因为不能匹配所以就原样返回了, 没有进行替换。

    str_replace()则仅对输入字符型向量的每个元素中模式的第一次出现进行替换, 不如str_replace_all()常用。

    函数str_remove_all()相当于在str_replace_all()中指定替换内容为空字符串。

    ## [1] "123456" "011"

    49.1.16 分组与捕获

    在正则表达式中用圆括号来分出组,

  • 确定优先规则;
  • 组成一个整体;
  • 拆分出模式中的部分内容(称为捕获);
  • 定义一段供后续引用或者替换。
  • 圆括号中的模式称为子模式,或者捕获

    49.1.16.1 确定优先级

    在使用备择模式时,James|Jim是在单词James和Jim之间选择。 如果希望选择的是中间的mesJi怎么办? 可以将备择模式保护起来, 如Ja(mes|Ji)m, 就可以确定备择模式的作用范围。

    正则表达式有比较复杂的优先级规则, 所以在不确定那些模式组合优先采纳时, 应该使用圆括号分组, 确定运算优先级。

    49.1.16.2 组成整体

    元字符问号、加号、星号、大括号等表示重复, 前面的例子中都是重复一个字符或者字符类。 如果需要重复由多个字符组成的模式, 如x[[:digit:]]{2}怎么办? 只要将该模式写在括号中,如:

    ## [1] │ <x01x02>
    ## [2] │ _<x11>x9

    上例的元数据中, 第一个元素重复了两次括号中的模式, 第二个元素仅有一次括号中的模式。

    用表示重复的元字符(+, *)重复某个模式时, 从第二次开始, 并不是要重复前面的字符或者子串, 而是重复前面的模式。 比如上例中x01x02

    49.1.16.3 捕获与向后引用

    有时一个模式中部分内容仅用于定位, 而实际有用的内容是其中的一部分, 就可以将这部分有用的内容包在圆括号中作为一个捕获。

    分组是自动编号的, 以左开括号的序号为准(除了作为选项、有名捕获等开扩号以外)。 在替换或者向后引用时, 可以用\1\2等表示匹配中第一个左括号对应的分组, 第二个左扩号对应的分组,……。

    比如,如果想严格重复前面的某个子字符串怎么办? 在模式中可以用\1, \2等表示严格重复前面捕获的子字符串, 这称为“向后引用”(back reference)。

    例如,([a-z]{3})\1这样的模式可以匹配如abcabc, uxzuxz这样的三字母重复:

    ## [1] │ <abcabc>

    又例如,下面的程序找出了年(后两位)、月、日数字相同的日期:

    ## [1] │ <2008-08-08>

    49.1.16.4 捕获与替换

    利用分组捕获可以对字符串进行修改。

    例:希望把带有前导零的数字的前导零删除,可以用如

    ## [2] │ <01204>
    ## [1] "1204"    "1204"    "001204B"

    上例的模式中的\b表示单词边界, 所以中间的0不会被当作前导零, 不是整个数字的也不会被修改。

    上例中的str_view()仅用于调试目的, 在进行替换时不是必要步骤。

    例:为了交换横纵坐标,可以用如下替换

    ## [1] │ 1st: <(5,3.6)>, 2nd: <(2.5, 1.1)>
    ## [1] "1st: (3.6, 5), 2nd: (1.1, 2.5)"

    例: 要匹配yyyy-mm-dd这样的日期, 并将其改写为mm/dd/yyyy, 就可以用这样的替换模式:

    ## [1] │ <1998-05-31>
    ## [2] │ <2017-01-14>
    ## [1] "05/31/1998" "01/14/2017"

    49.1.16.5 命名捕获

    可以用(?<name>...)这样的格式对捕获命名, 而不是仅根据开括号的序号区分。

    ## [1] │ <1998-05-31>
    ## [2] │ <2017-01-14>

    str_replace_all()还不支持使用命名捕获作为替换。

    49.1.16.6 非捕获分组

    如果某个分组仅想起到分组作用但是不会提取具体的匹配内容也不会用该组内容做替换, 可以将该组变成“非捕获分组”, 办法是把表示分组开始左圆括号变成(?:三个字符。 这在用分组表示优先级时比较有用, 如"Jam(es|Ji)m"可以写成"Jam(?:es|Ji)m"。 非捕获分组在向后引用和替换时不计入\1\2这样的排列中。

    比如,把1921-2020之间的世纪号删去,可以用

    ## [1] │ <1978>
    ## [2] │ <2017>
    ## [3] │ <2035>
    ## [1] "78" "17" "35"

    其中用了非捕获分组使得备择模式19|20优先匹配。 注意模式并没有能保证日期在1921-2020之间。更周密的程序可以写成:

    ## [1] │ <1978>
    ## [2] │ <2017>
    str_replace_all(pat1, repl) |> str_replace_all(pat2, repl)
    ## [1] "78"   "17"   "2035"

    49.1.16.7 提取捕获内容

    为了找到字符型向量中哪些元素能匹配模式, 可以用 str_subset() , str_which() , str_detect()

    为了提取字符型向量中匹配模式的部分子串, 可以用 str_extract() str_extract_all()

    为了提取字符型向量中匹配模式的部分以及捕获的部分, 可以用 str_match() str_match_all()

    参见 49.2

    49.1.16.8 非匹配向前向后偷窥

    如果要提取的一个模式以某个后缀作为标志, 可以将这个后缀作为模式的一部分, 然后仅捕获该后缀之前的部分。 对 http://baidu.com , https://baidu.com , ftp://baidu.com 等网址, 希望提取 http , https , ftp 等协议, 可以匹配到冒号为止:

    ## [1] │ <http:>//baidu.com
    ## [2] │ <https:>//baidu.com
    ## [3] │ <ftp:>//baidu.com

    这样的模式多匹配了一个“ : ”号。 可以用 str_match() 函数提取其中的捕获:

    ##      [,1]     [,2]   
    ## [1,] "http:"  "http" 
    ## [2,] "https:" "https"
    ## [3,] "ftp:"   "ftp"

    结果是字符型矩阵,第二列是所需的内容。 但这样还是在整个匹配中包含了不需要的内容。

    可以用 (?=) 包含要提取的模式的标志性后缀, 但匹配结果不包含这个后缀,如:

    pat2 <- r"{^(.+)(?=:)}"
    str_view(addrs, pat2)
    ## [1] │ <http>://baidu.com
    ## [2] │ <https>://baidu.com
    ## [3] │ <ftp>://baidu.com

    可见这次没有匹配“ : ”部分。 提取只要用 str_extract()

    ## [1] "http"  "https" "ftp"
    如果需要提取的部分以某个前缀为标志, 可以用 (?<=) 包含这个前缀, 最后匹配的模式不不含这个前缀。 设输入的字符串向量 addrs 中都是一些网址, 希望仅匹配 // 后面的主要部分,
    ## [1] │ http://<baidu.com>
    ## [2] │ https://<baidu.com>
    ## [3] │ ftp://<baidu.com>

    可见仅匹配了 // 后面的内容。

    ## [1] "baidu.com" "baidu.com" "baidu.com"

    还可以用 (?!) 包含不允许的后缀, 用 (?<!) 包含不允许的前缀。

    49.1.17 在数据框中拆分列

    tidyr::separate_wider_regex() 可以对一个字符型列用指定的模式拆分出其中的内容。 有如下的数据:

    49.2.1 str_view() 函数

    str_view(string, pattern) 在RStudio中加亮显示 pattern 给出的正则表达式模式在 string 中的所有匹配,并将每个匹配部分用 <> 界定。 在其它输出中不加亮显示, 仅使用 <> 界定。 string 是输入的源字符型向量。

    如果要匹配的是固定字符串, 写成 str_view(string, fixed(pattern))

    如果要匹配的是单词等的边界, 模式用 boundary() 函数表示,如 str_view("a brown fox", boundary("word")) 将匹配首个单词。

    49.2.2 regex() 函数

    stringr包用到正则表达式模式的地方, 实际上应该写成 regex(pattern) , 只写模式本身是一种简写。 regex() 函数可以指定 ignore_case=TRUE 要求不区分大小写, 指定 multi_line=TRUE 使得 ^ $ 匹配用换行符分开的每行的开头和结尾, dotall=TRUE 使得 . 能够匹配换行符。 comment=TRUE 使得模式可以写成多行, 行尾的井号后面表示注释, 这时空格不再原样匹配, 为了匹配空格需要写在方括号内或者用反斜杠开头。

    regex() 类似的表示模式的函数有 fixed() boundary() coll()

    49.2.3 构造复杂的模式

    可以用 paste0() 将多个字符串连接在一起构成一个模式。 可以在 regex() 中用 comment = TRUE 将模式写成多行带有行尾注释的形式。 例如,年月日格式的日期:

    ## [1] │ <1978-1-03>
    ## [2] │ <1911-07-31>
    ## [3] │ <2023-10-18>

    可以用 str_flatten(subpats, sep) 将保存在向量 subpats 中的元素用 sep 分隔符连接起来, 组成一个长模式。 为防止输入的内容有元字符, 可以用 str_escape() 进行转义。 str_escape() 将输入内容中的正则表达式元字符转义为原样匹配,

    ## [1] "x\\[1\\] = myfile\\.txt"

    例:要匹配保存在字符型向量中的任何一个颜色名, 将这些颜色名用 | 连接成一个长的正则表达式后匹配。

    ## [1] │ A <red> fox
    ## [3] │ <Green> sleeves

    49.2.4 检查哪些元素能够匹配

    str_detect(string, pattern) 返回字符型向量 string 的每个元素是否匹配 pattern 中的模式的逻辑型结果。 与基本R的 grepl() 作用类似。

    x <- c("New theme", "Old times", "In the present theme")
    str_view(x, "the")
    ## [1] │ New <the>me
    ## [3] │ In <the> present <the>me
    ## [1]  TRUE FALSE  TRUE

    上例中的 str_view() 仅用作调试目的。

    当要查找的内容是tibble的一列时, 用 filter() str_detct() 配合, 可以进行行子集选择。 比如,在数据框的人名中查找中间有空格的名字:

    ## # A tibble: 1 × 1
    ##   name  
    ##   <chr> 
    ## 1 李  明

    可以在 dplyr::summarize() 中用 sum(str_detect(x, pat)) 计算符合模式的观测个数, 用 mean 计算比例。

    因为 str_detect() 返回与输入源字符串向量等长的逻辑型向量, 所以特别适用于用逻辑运算来满足复杂的条件。 在姓名中找到同时有 a y 的:

    ## [1] "Kathy" "Mary"  "Sandy"

    a 或有 y 的:

    ## [1] "Becka" "Gail"  "Karen" "Kathy" "Mary"  "Sandy"

    用这种方法可以将复杂的匹配条件拆分为多个条件, 并用与、或、非组合起来。

    49.2.5 计数模式匹配次数

    str_count() 则返回模式在每个元素中匹配的次数。

    ## [1] 6 3 0

    正则表达式在匹配时, 是否允许同一源字符串中的两次匹配有重叠部分? 这是不允许的。

    str_view("1212121", "121")
    ## [1] │ <121>2<121>
    ## [1] 2

    如果允许重叠, 就可以有3次 121 模式。

    49.2.6 返回匹配的元素或元素序号

    str_subset(string, pattern) 返回字符型向量中能匹配 pattern 的那些元素组成的子集, 与基本R函数 grep(pattern, string, value=TRUE) 效果相同。 注意,返回的是整个元素而不是匹配的子串。

    比如,查找人名中间有空格的:

    ## [2] │ [<李  明>]
    ## [1] "[李  明]"

    注意上例中仅返回了有匹配的元素, 而且是匹配元素的整个字符串而不是匹配的部分。

    str_which(string, pattern) 返回字符型向量 string 的元素当中能匹配 pattern 中的模式的元素序号。 与基本R的 grep() 作用类似。

    x <- c("New theme", "Old times", "In the present theme")
    str_which(x, "the")
    ## [1] 1 3

    49.2.7 提取匹配内容

    str_subset() 返回的是有匹配的源字符串, 而不是匹配的部分子字符串。 用 str_extract(string, pattern) 从源字符串中取出 首次 匹配的子串, 没有匹配则不返回结果。 结果是一个与 str_subset() 长度相同的字符串向量, 仅匹配元素的首个匹配子串被返回。 因为输出长度不一定等于输入长度, 所以这个函数应该慎用。

    ## [1] │ A f<all>ing b<all>
    ## [1] "all"

    注意第二个输入字符串不匹配所有返回结果中不包含该元素的对应输出。 这会造成结果的对应困难, 所以应仅在确保所有输入都能匹配时才使用 str_extract() 这个函数, 一般应使用 str_extract_all()

    str_extract_all(string, pattern) 取出所有匹配子串, 结果是一个列表, 长度与输入的字符型向量 string 的元素个数相等, 列表的每个元素对应于 string 的每个元素。 结果列表的每个元素是一个字符型数组, 存放所有匹配的子字符串, 没有匹配的输入字符串对应的输出元素是空向量(长度为0的向量)。

    x <- c("A falling ball", "Phone call.", "case")
    str_view(x, "all")
    ## [1] │ A f<all>ing b<all>
    ## [2] │ Phone c<all>.
    ## [[1]]
    ## [1] "all" "all"
    ## [[2]]
    ## [1] "all"
    ## [[3]]
    ## character(0)

    str_extract_all() 可以加选项 simplyfy=TRUE , 使得返回结果变成一个字符型矩阵, 每行是原来一个元素中取出的各个子串, 列数等于最大匹配次数, 没有那么多匹配次数的填以空字符串。 如果正常匹配结果不会出现空字符就可以用这种方法简化结果的保存和访问。

    ##      [,1]  [,2] 
    ## [1,] "all" "all"
    ## [2,] "all" ""   
    ## [3,] ""    ""

    49.2.8 提取分组捕获内容

    str_subset() 提取的是能匹配模式的元素子集, 而不是匹配的模式或者捕获; str_extract() str_extract_all() 提取的是每个元素的首次或者所有匹配的子字符串, 而不是其中的捕获。

    str_match(string, pattern) 提取每个元素的首次匹配内容以及其中各个捕获分组内容, 结果是一个矩阵, 行数等于输入的字符型向量 string 的元素个数, 矩阵每行对应于向量 string 中的一个元素, 每行第一个元素是匹配内容,其它元素是各个捕获, 没有则为字符型缺失值(不是空字符串)。

    比如,希望匹配中间有空格的人名并捕获空格前后部分:

    ##      [,1]     [,2] [,3]
    ## [1,] NA       NA   NA  
    ## [2,] "李  明" "李" "明"

    上例中源数据第一个元素没有匹配, 所以结果都是缺失值 NA , 第二个元素的结果在第二行, 首先是整个匹配的子字符串, 然后是捕获的两个部分。

    stringr::str_match_all(string, pattern) 匹配每个字符串中所有出现位置, 结果是一个列表, 每个列表元素对应于输入的字符型向量 string 的每个元素, 结果中每个列表元素是一个字符型矩阵, 用来保存所有各个匹配以及匹配中的捕获, 每行是一个匹配的结果,首先是匹配结果,其次是各个捕获。 结果列表中每个作为列表元素的矩阵大小不一定相同。 当某个元素完全没有匹配时, 结果列表中对应元素是行数为0的矩阵。

    比如,模式为19xx或者20xx的年份, 并将其分为前两位和后两位:

    ## [1] │ <1978>-<2000>
    ## [2] │ <2011>-<2020>-<2099>
    ## [[1]]
    ##      [,1]   [,2] [,3]
    ## [1,] "1978" "19" "78"
    ## [2,] "2000" "20" "00"
    ## [[2]]
    ##      [,1]   [,2] [,3]
    ## [1,] "2011" "20" "11"
    ## [2,] "2020" "20" "20"
    ## [3,] "2099" "20" "99"
    ## [[3]]
    ##      [,1] [,2] [,3]

    下面的程序合并上面提取的年份后两位为一个字符型向量:

    ## [1] "78" "00" "11" "20" "99"

    用基本R功能:

    ## [1] "78" "00" "11" "20" "99"

    49.2.9 定位匹配位置

    str_locate(string, pattern) 对输入字符型向量 string 的每个元素返回首次匹配 pattern 的开始和结束位置。 输出结果是一个两列的矩阵,每行对应于输入的一个元素, 每行的两个元素分别是首次匹配的开始和结束字符序号(按字符计算)。如

    x <- c("A falling ball", "Phone call.")
    str_view(x, "all")
    ## [1] │ A f<all>ing b<all>
    ## [2] │ Phone c<all>.
    ##      start end
    ## [1,]     4   6
    ## [2,]     8  10

    str_locate_all(string, pattern) 则可以返回每个元素中所有匹配的开始和结束位置, 结果是一个列表, 每个列表元素对应于输入字符型向量的每个元素, 结果中每个列表元素是一个两列的数值型矩阵, 每行为一个匹配的开始和结束字符序号。如

    ## [1] │ A f<all>ing b<all>
    ## [2] │ Phone c<all>.
    ## [[1]]
    ##      start end
    ## [1,]     4   6
    ## [2,]    12  14
    ## [[2]]
    ##      start end
    ## [1,]     8  10

    注意如果需要取出匹配的元素可以用 str_subset() , 要取出匹配的子串可以用 str_extract() str_extract_all() , 取出匹配的子串以及分组捕获可以用 str_match() str_match_all()

    49.3 附录:利用基本R函数进行正则表达式处理

    基本R函数 grep , sub , gsub , regexpr , gregexpr , regexec 中的 pattern 参数可以是正则表达式, 这时应设参数 fixed=FALSE strsplit 函数中的参数 split 也可以是正则表达式。 regmatches 函数从 regexpr , gregexpr , regexec 的结果中提取匹配的字符串。

    以原样匹配为例。

    ## [1]  5 -1  4
    ## attr(,"match.length")
    ## [1]  3 -1  3
    ## attr(,"index.type")
    ## [1] "chars"
    ## attr(,"useBytes")
    ## [1] TRUE

    这里使用了 regexpr 函数。 regexpr 函数的一般用法为:

    自变量为:

  • pattern 是一个正则表达式,如果用了 fixed=TRUE 选项,则当作普通原样文本来匹配;
  • text 是源字符串向量,要从其每个元素中查找pattern模式出现的位置;
  • ignore.case:是否要忽略大小写匹配;
  • perl 选择是否采用perl格式,如果不把pattern当作普通原样文本,应该选 perl=TRUE ,perl语言的正则表达式是事实上的标准,所以这样兼容性更好;
  • fixed 当 fixed=TRUE pattern 作为普通原样文本解释;
  • useBytes 为TRUE时逐字节进行匹配,否则逐字符进行匹配。之所以有这样的区别,是因为有些编码中一个字符由多个字节构成,BGK编码的汉字由两个字节组成,UTF-8编码的汉字也是由两个字节构成。
  • regexpr() 函数返回一个整数值的向量, 长度与 text 向量长度相同, 结果的每个元素是在 text 的对应元素中 pattern 的首次匹配位置; 没有匹配时结果元素取-1。 结果会有一个 match.length 属性,表示每个匹配的长度, 无匹配时取-1。

    如果仅关心源字符串向量 text 中哪些元素能匹配 pattern , 可以用 grep 函数,如

    ## [1] 1 3

    结果说明源字符串向量的三个元素中仅有第1、第3号元素能匹配。 如果都不匹配,返回 integer(0)

    grep 可以使用与 regexpr 相同的自变量, 另外还可以加选项 invert=TRUE ,这时返回的是不匹配的元素的下标。

    grep() 如果添加选项 value=TRUE , 则结果不是返回有匹配的元素的下标而是返回有匹配的元素本身(不是匹配的子串),

    ## [1] "New theme"            "In the present theme"

    grepl 的作用与 grep 类似, 但是其返回值是一个长度与源字符串向量 text 等长的逻辑型向量, 每个元素的真假对应于源字符串向量中对应元素的匹配与否。如

    ## [1]  TRUE FALSE  TRUE

    就像 grep() grepl() 本质上给出相同的结果,只是结果的表示方式不同, regexec() regexpr() 也给出仅在表示方式上有区别的结果。 regexpr() 主要的结果是每个元素的匹配位置, 用一个统一的属性返回各个匹配长度; regexec() 则返回一个与源字符串向量等长的列表, 列表的每个元素为匹配的位置,并且列表的每个元素有匹配长度作为属性。 所以,这两个函数只需要用其中一个就可以,下面仅使用 regexpr() regexec() 的使用效果如

    ## [[1]]
    ## [1] 5
    ## attr(,"match.length")
    ## [1] 3
    ## attr(,"useBytes")
    ## [1] TRUE
    ## attr(,"index.type")
    ## [1] "chars"
    ## [[2]]
    ## [1] -1
    ## attr(,"match.length")
    ## [1] -1
    ## attr(,"useBytes")
    ## [1] TRUE
    ## attr(,"index.type")
    ## [1] "chars"
    ## [[3]]
    ## [1] 4
    ## attr(,"match.length")
    ## [1] 3
    ## attr(,"useBytes")
    ## [1] TRUE
    ## attr(,"index.type")
    ## [1] "chars"

    grep() , grepl() , regexpr() , regexec() 都只能找到源字符串向量的每个元素中模式的首次匹配, 不能找到所有匹配。 gregexpr() 函数可以找到所有匹配。

    ## [[1]]
    ## [1] 5
    ## attr(,"match.length")
    ## [1] 3
    ## attr(,"index.type")
    ## [1] "chars"
    ## attr(,"useBytes")
    ## [1] TRUE
    ## [[2]]
    ## [1] -1
    ## attr(,"match.length")
    ## [1] -1
    ## attr(,"index.type")
    ## [1] "chars"
    ## attr(,"useBytes")
    ## [1] TRUE
    ## [[3]]
    ## [1]  4 16
    ## attr(,"match.length")
    ## [1] 3 3
    ## attr(,"index.type")
    ## [1] "chars"
    ## attr(,"useBytes")
    ## [1] TRUE

    其结果是一个与源字符串向量等长的列表, 格式与 regexec() 的结果格式类似, 列表的每个元素对应于源字符串向量的相应元素, 列表元素值为匹配的位置, 并有属性 match.length 保存了匹配长度。 匹配位置和匹配长度包含了所有的匹配, 见上面例子中第三个元素的匹配结果。

    函数 grep , grepl 结果仅给出每个元素能否匹配。 regexpr() , regexec() , gregexpr() 则包含了匹配位置与匹配长度, 这时,可以用 regmatches() 函数取出具体的匹配字符串。 regmatches() 一般格式为

    regmatches(x, m, invert = FALSE)

    其中 x 是源字符串向量, m regexpr() regexec() gregexpr() 的匹配结果。

    ## [1] "the" "the"

    可以看出, regmatches() 仅取出有匹配时的匹配内容, 无匹配的内容被忽略。

    取出多处匹配的例子如:

    ## [[1]]
    ## [1] "the"
    ## [[2]]
    ## character(0)
    ## [[3]]
    ## [1] "the" "the"

    regmatches() 第二个自变量是 gregexpr() 的结果时, 其输出结果变成一个列表, 并且不再忽略无匹配的元素, 无匹配元素对应的列表元素为 character(0) , 即长度为零的字符型向量。 对有匹配的元素, 对应的列表元素为所有的匹配字符串组成的字符型向量。

    如果 pattern 中没有正则表达式, grep() , grepl() , regexpr() , gregexpr() 中都可以用 fixed=TRUE 参数取代 perl=TRUE 参数, 这时匹配总是解释为原样匹配, 即使 pattern 中包含特殊字符也是进行原样匹配。

    49.3.1 不区分大小写匹配

    在基本R中, 为了不区分大小写匹配, 可以在 grep 等函数调用时加选项 ignore.case=TRUE

    grep("Dr", c("Dr. Wang", "DR. WANG", "dR. W.R."))
    ## [1] 1
    ## [1] 1 2 3
    ## [1] 1 2 3

    49.3.2 匹配单个字符

    在模式中用“.”匹配任意一个字符(除了换行符 "\n" ,能否匹配此字符与选项有关)。如

    ## [1]  1  2 -1
    ## attr(,"match.length")
    ## [1]  3  3 -1
    ## attr(,"index.type")
    ## [1] "chars"
    ## attr(,"useBytes")
    ## [1] TRUE

    regexpr 仅给出每个元素中模式的首次匹配位置而不是给出匹配的内容。 regmatches 函数以原始字符型向量和匹配结果为输入, 结果返回每个元素中匹配的各个子字符串(不是整个元素),如:

    ## [1] "abc" "abs"

    注意返回结果和输入字符型向量元素不是一一对应的,仅返回有匹配的结果。

    像句点这样的字符称为元字符(meta characters), 在正则表达式中有特殊函数。 如果需要匹配句点本身,用“ [.] ”或者“ \. ”表示。 比如,要匹配 a.txt 这个文件名,如下做法有错误:

    grep("a.txt", c("a.txt", "a0txt"), perl=TRUE)
    ## [1] 1 2

    结果连 a0txt 也匹配了。用“[.]”表示句点则将句点不做特殊解释:

    ## [1] 1
    ## [1] 1

    注意在R语言字符型常量中一个 \ 需要写成两个。

    如果仅需按照原样进行查找, 也可以在 grep() , grepl() regexpr() gregexpr() 等函数中加选项 fixed=TRUE , 这时不要再用 perl=TRUE 选项。

    grep("a.txt", c("a.txt", "a0txt"), fixed=TRUE)
    ## [1] 1

    49.3.3 匹配一组字符中的某一个

    模式“ [ns]a.[.]xls ” 表示匹配的第一个字符是 n s , 第二个字符是 a ,第三个字符任意,第四个字符是句点, 然后是 xls

    ## [1] 1 2 1
    ## attr(,"match.length")
    ## [1] 7 7 7
    ## attr(,"index.type")
    ## [1] "chars"
    ## attr(,"useBytes")
    ## [1] TRUE

    49.3.4 原样匹配元字符

    ## [1] 2

    也可以用“ [[] ”表示“ [ ”, 用“ []] ”表示“ ] ”,如

    grep("int x[[]5[]]", c("int x;", "int x[5]"), perl=TRUE)
    ## [1] 2

    49.3.5 匹配数字

    ## [1] 1

    49.3.6 匹配开头和末尾

    ## [1] 1
    ## [1] 1

    只匹配了第一个输入字符串。

    49.3.7 匹配字母、数字、下划线

    ## [1] "s1."

    可以看出,模式匹配了 s1. 而没有匹配 s#.

    49.3.8 十六进制和八进制数

    ## [1] 4 8
    ## attr(,"match.length")
    ## [1] 1 1
    ## attr(,"index.type")
    ## [1] "chars"
    ## attr(,"useBytes")
    ## [1] TRUE

    匹配了两个换行符。

    49.3.9 POSIX字符类

    ## [1] 1 2 3 4

    49.3.10 加号重复匹配

    ## [1] "sa1"   "sa123"
    ## [1] "abc123@efg.com"

    匹配的电子邮件地址在 @ 前面可以使用任意多个字母、数字、下划线, 在 @ 后面由小数点分成两段, 每段可以使用任意多个字母、数字、下划线。 这里用了 ^ $ 表示全字符串匹配。

    49.3.11 星号和问号重复匹配

    ^https?://[[:alnum:]./]+$ 可以匹配http或https开始的网址。

    ## [1] 1 2

    (注意第二个字符串不是合法网址但是按这个正则表达式也能匹配)

    49.3.12 计数重复

    grep("[[:digit:]]{3}", c("1", "12", "123", "1234"))
    ## [1] 3 4

    模式匹配的是三位的数字。

    日期匹配例:

    ## [1] 1 2

    49.3.13 贪婪匹配和懒惰匹配

    ## [1] "<B>1st</B> other <B>2nd</B>"

    我们本来期望的是提取第一个“ <B> …… </B> ”组合, 不料提取了两个“ <B> …… </B> ”组合以及中间的部分。

    比如,上例中模式修改后得到了期望的结果:

    ## [1] "<B>1st</B>"

    49.3.14 单词边界

    ## [1] 1

    49.3.15 句点全匹配与多行模式

    句点通配符一般不能匹配换行,如

    ## integer(0)

    跨行匹配失败。而在HTML的规范中换行是正常的。 一种办法是预先用 gsub 把所有换行符替换为空格。 但是这只能解决部分问题。

    另一方法是在Perl正则表达式开头添加 (?s) 选项, 这个选项使得句点通配符可以匹配换行符。

    ## [1] "<B>1st\n</B>"

    多行模式例:

    ## [1] "<B>"
    ## [1] "<B>"  "</B>"

    字符串 s 包含两行内容,中间用 \n 分隔。 mres1 的匹配模式没有打开多行选项, 所以模式中的 ^ 只能匹配 s 中整个字符串开头。 mres2 的匹配模式打开了多行选项, 所以模式中的 ^ 可以匹配 s 中每行的开头。

    49.3.16 备择模式

    例如,某个人的名字用James和Jim都可以, 表示为 James|Jim , 如

    ## [[1]]
    ## [1] "James"
    ## [[2]]
    ## [1] "Jim"

    49.3.17 分组与捕获

    希望把“ <B> …… </B ”两边的“ <B> ”和“ </B> ”删除, 可以用如下的替换方法:

    ## [1] "1st other 2nd"

    替换模式中的 \1 (写成R字符型常量时 \ 要写成 \\ )表示第一个圆括号匹配的内容, 但是表示选项的圆括号( (?s) )不算在内。

    例:希望把带有前导零的数字的前导零删除,可以用如

    ## [1] "123" "123" "123"

    其中的 \b 模式表示单词边界, 这可以排除在一个没有用空格或标点分隔的字符串内部拆分出数字的情况。

    例:为了交换横纵坐标,可以用如下替换

    ## [1] "1st: (3.6, 5), 2nd: (1.1, 2.5)"

    例如,要匹配yyyy-mm-dd这样的日期, 并将其改写为mm/dd/yyyy, 就可以用这样的替换模式:

    ## [1] "05/31/1998" "01/14/2017"

    分组除了可以做替换外, 还可以用来表示模式中的重复出现内容。 例如, ([a-z]{3})\1 这样的模式可以匹配如 abcabc , uxzuxz 这样的三字母重复。如

    ## [1] 1

    又例如,下面的程序找出了年(后两位)、月、日数字相同的日期:

    ## [1] "2008-08-08"

    下面是一个非捕获分组示例。 设需要把1921-2020之间的世纪号删去,可以用

    ## [1] "78" "17" "35"

    其中用了非捕获分组使得备择模式 19|20 优先匹配。 注意模式并没有能保证日期在1921-2020之间。更周密的程序可以写成: