2009-06-04 112 views
67

在R中,是否可以从正则表达式匹配中提取组捕获?据我所知,没有grepgrepl,regexpr,gregexpr,subgsub返回组捕获。R中的正则表达式组捕获多个捕获组

我需要提取从字符串键 - 值对被这样编码:

\((.*?) :: (0\.[0-9]+)\) 

我总是可以只是做多个全匹配里grep,或做一些外(非-R)的处理,但我希望我可以在R内完成所有工作。是否有一个函数或一个包提供了这样的功能?

回答

88

str_match(),从stringr包,将做到这一点。它返回一个字符矩阵的比赛中为每个组一列(一个用于整场比赛):

> s = c("(sometext :: 0.1231313213)", "(moretext :: 0.111222)") 
> str_match(s, "\\((.*?) :: (0\\.[0-9]+)\\)") 
    [,1]       [,2]  [,3]   
[1,] "(sometext :: 0.1231313213)" "sometext" "0.1231313213" 
[2,] "(moretext :: 0.111222)"  "moretext" "0.111222"  
+1

这实际上正是我所需要的(当我最初问这个问题时)。标记为未来参考。谢谢。 – 2012-04-06 20:35:20

34

GSUB做到这一点,从您的例子:

gsub("\\((.*?) :: (0\\.[0-9]+)\\)","\\1 \\2", "(sometext :: 0.1231313213)") 
[1] "sometext 0.1231313213" 

你需要加倍引号然后他们对正则表达式的工作逃脱\ S。

希望这会有所帮助。

+0

其实我需要把捕获的子串放到data.frame中。但是,看看你的答案,我想我可以链接gsub和几个strsplit来得到我想要的,也许: strsplit(strsplit(gsub(正则表达式,“\\ 1 :: \\ 2 ::::” ,str),“::::”)[[1]],“::”) – 2009-06-05 16:03:18

+5

太好了。 R`gsub`手册页非常需要一个示例,显示您需要'\\ 1'来转义捕获组引用。 – smci 2014-03-26 15:51:45

2

这就是我最终解决这个问题的方法。我用了两个独立的正则表达式匹配的第一和第二捕捉组和运行两个gregexpr电话,然后拉出匹配的字符串:

regex.string <- "(?<=\\().*?(?= ::)" 
regex.number <- "(?<= ::)\\d\\.\\d+" 

match.string <- gregexpr(regex.string, str, perl=T)[[1]] 
match.number <- gregexpr(regex.number, str, perl=T)[[1]] 

strings <- mapply(function (start, len) substr(str, start, start+len-1), 
        match.string, 
        attr(match.string, "match.length")) 
numbers <- mapply(function (start, len) as.numeric(substr(str, start, start+len-1)), 
        match.number, 
        attr(match.number, "match.length")) 
+0

对于工作代码+1。但是,我宁愿从R运行一个快速shell命令,并使用像这样的`expr“xyx0.0023xyxy”:'[^ 0-9] * \([。0-9] \ + \) '' – 2011-09-01 23:18:27

15

GSUB()可以做到这一点,只返回捕获组:

但是,为了使其发挥作用,您必须按照gsub()帮助中的说明明确选择捕获组外部的元素。

(...)未被替换的字符向量'x'的元素将不会被返回。

因此,如果您要选择的文本位于某个字符串的中间,那么在捕获组之前和之后添加。*应允许您只返回它。

gsub(".*\\((.*?) :: (0\\.[0-9]+)\\).*","\\1 \\2", "(sometext :: 0.1231313213)") [1] "sometext 0.1231313213"

16

尝试regmatches()regexec()

regmatches("(sometext :: 0.1231313213)",regexec("\\((.*?) :: (0\\.[0-9]+)\\)","(sometext :: 0.1231313213)")) 
[[1]] 
[1] "(sometext :: 0.1231313213)" "sometext"     "0.1231313213" 
3

我喜欢Perl兼容的正则表达式。也许别人不会太...

这里是做Perl兼容的正则表达式和匹配,我用其他语言的函数功能的函数:

regexpr_perl <- function(expr, str) { 
    match <- regexpr(expr, str, perl=T) 
    matches <- character(0) 
    if (attr(match, 'match.length') >= 0) { 
    capture_start <- attr(match, 'capture.start') 
    capture_length <- attr(match, 'capture.length') 
    total_matches <- 1 + length(capture_start) 
    matches <- character(total_matches) 
    matches[1] <- substr(str, match, match + attr(match, 'match.length') - 1) 
    if (length(capture_start) > 1) { 
     for (i in 1:length(capture_start)) { 
     matches[i + 1] <- substr(str, capture_start[[i]], capture_start[[i]] + capture_length[[i]] - 1) 
     } 
    } 
    } 
    matches 
} 
0

正如stringr包建议这可以使用str_match()str_extract()来实现。

library(stringr) 

strings <- c(" 219 733 8965", "329-293-8753 ", "banana", 
      "239 923 8115 and 842 566 4692", 
      "Work: 579-499-7527", "$1000", 
      "Home: 543.355.3679") 
phone <- "([2-9][0-9]{2})[- .]([0-9]{3})[- .]([0-9]{4})" 

提取和我们的团体组合:

str_extract(strings, phone) 
# [1] "219 733 8965" "329-293-8753" NA    "239 923 8115" "579-499-7527" NA    
# [7] "543.355.3679" 

指示组,输出矩阵(我们感兴趣的列2+):

从手动改编

str_match(strings, phone) 
#  [,1]   [,2] [,3] [,4] 
# [1,] "219 733 8965" "219" "733" "8965" 
# [2,] "329-293-8753" "329" "293" "8753" 
# [3,] NA    NA NA NA  
# [4,] "239 923 8115" "239" "923" "8115" 
# [5,] "579-499-7527" "579" "499" "7527" 
# [6,] NA    NA NA NA  
# [7,] "543.355.3679" "543" "355" "3679" 
0

strcapture解决方案从utils

x <- c("key1 :: 0.01", 
     "key2 :: 0.02") 
strcapture(pattern = "(.*) :: (0\\.[0-9]+)", 
      x = x, 
      proto = list(key = character(), value = double())) 
#> key value 
#> 1 key1 0.01 
#> 2 key2 0.02