R中具有多个捕获组的正则表达式组捕获


94

在R中,是否可以从正则表达式匹配中提取组捕获?据我所知,没有一个grepgreplregexprgregexprsub,或gsub返回组捕获。

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

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

我总是可以做多个完全匹配的抓图,或者做一些外部(非R)处理,但是我希望可以在R中完成所有这些工作。是否有提供此功能的函数或程序包?

Answers:


118

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
str_match_all()匹配正则表达式中的所有组
smci 2014年

如何只打印[,1]的捕获组?
nenur

不确定你在找什么。捕获的组是第2列和第3列[,1][,2:3]是捕获的组。
肯特·约翰逊,

50

gsub通过您的示例执行此操作:

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

您需要将引号中的\ s进行两次转义,然后它们才能使用正则表达式。

希望这可以帮助。


实际上,我需要拉出捕获的子字符串以放入data.frame。但是,看看您的回答,我想我可以将gsub和几个strsplit链接起来以获得我想要的东西,也许是:strsplit(strsplit(gsub(regex,“ \\ 1 :: \\ 2 ::::”,str ),“ ::::”)[[1]],“ ::”)
丹尼尔·迪克森

8
大。R gsub联机帮助页非常需要一个示例,该示例显示您需要'\\ 1'来转义捕获组引用。
smci 2014年

33

尝试regmatches()regexec()

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

3
感谢香草R解决方案,并指出regmatches我从未见过的内容
Andy

为什么要写两次字符串?
Stefano Borini,

@StefanoBorini regexec返回一个列表,其中仅包含有关比赛位置的信息,因此regmatches要求用户提供比赛列表所属的字符串。
RTbecard

19

gsub()可以执行此操作,并且仅返回捕获组:

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

(...)不替换的字符向量'x'的元素将保持不变。

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

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


4

我喜欢与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
}

3

这就是我最终解决此问题的方式。我使用了两个单独的正则表达式来匹配第一个和第二个捕获组并运行两次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"))

+1为有效代码。但是,我宁愿运行来自R快速shell命令,并使用猛砸的一行像这样expr "xyx0.0023xyxy" : '[^0-9]*\([.0-9]\+\)'
亚历山大Levchuk

3

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

2

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_all(strings, phone, simplify=T)
#      [,1]           [,2]          
# [1,] "219 733 8965" ""            
# [2,] "329-293-8753" ""            
# [3,] ""             ""            
# [4,] "239 923 8115" "842 566 4692"
# [5,] "579-499-7527" ""            
# [6,] ""             ""            
# [7,] "543.355.3679" ""   

用输出矩阵指示组(我们对第2+列感兴趣):

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

842 566 4692
Ferroao

感谢您的疏忽。使用_all相关stringr功能的后缀进行了更正。
威震天

0

可以使用unglue软件包完成此操作,以所选答案中的示例为例:

# install.packages("unglue")
library(unglue)

s <- c("(sometext :: 0.1231313213)", "(moretext :: 0.111222)")
unglue_data(s, "({x} :: {y})")
#>          x            y
#> 1 sometext 0.1231313213
#> 2 moretext     0.111222

或者从数据帧开始

df <- data.frame(col = s)
unglue_unnest(df, col, "({x} :: {y})",remove = FALSE)
#>                          col        x            y
#> 1 (sometext :: 0.1231313213) sometext 0.1231313213
#> 2     (moretext :: 0.111222) moretext     0.111222

您可以从unglue模式中获取原始正则表达式,可以选择使用名为capture:

unglue_regex("({x} :: {y})")
#>             ({x} :: {y}) 
#> "^\\((.*?) :: (.*?)\\)$"

unglue_regex("({x} :: {y})",named_capture = TRUE)
#>                     ({x} :: {y}) 
#> "^\\((?<x>.*?) :: (?<y>.*?)\\)$"

更多信息:https : //github.com/moodymudskipper/unglue/blob/master/README.md

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.