2009-06-04 37 views
67

Trong R, có thể trích xuất nhóm chụp từ một biểu thức chính quy không? Theo như tôi có thể biết, không ai trong số grep, grepl, regexpr, gregexpr, sub hoặc gsub trả lại các ảnh chụp nhóm.Chụp nhóm Regex trong R với nhiều nhóm chụp

tôi cần phải giải nén cặp khóa-giá trị từ chuỗi được mã hóa như sau:

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

tôi có thể luôn luôn chỉ làm nhiều greps toàn trận đấu, hoặc làm một số bên ngoài (không-R) chế biến, nhưng tôi đã hy vọng tôi có thể làm tất cả trong R. Có một chức năng hoặc một gói cung cấp một chức năng như vậy để làm điều này?

Trả lời

88

str_match(), thuộc về gói stringr, sẽ làm điều này. Nó trả về một ma trận nhân vật với một cột cho mỗi nhóm trong trận đấu (và một cho toàn bộ trận đấu):

> 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

Đây là thực tế chính xác những gì tôi cần (trở lại khi tôi hỏi câu hỏi ban đầu). Đánh dấu là được chấp nhận để tham khảo trong tương lai. Cảm ơn. –

+0

và 'str_match_all()' để phù hợp với tất cả các nhóm trong một regex – smci

34

gsub thực hiện điều này, từ ví dụ của bạn:

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

bạn cần phải tăng gấp đôi thoát khỏi \ s trong dấu ngoặc kép sau đó họ làm việc cho các regex.

Hy vọng điều này sẽ hữu ích.

+0

Thực ra tôi cần phải kéo các chất nền đã chụp để đưa vào một tệp dữ liệu. Nhưng, nhìn vào câu trả lời của bạn, tôi đoán tôi có thể chuỗi gsub và một vài strsplit để có được những gì tôi muốn, có thể: strsplit (strsplit (gsub (regex, "\\ 1 :: \\ 2 ::::" , str), "::::") [[1]], "::") –

+5

Tuyệt vời. R 'gsub' manpage rất nặng cần một ví dụ cho thấy bạn cần '\\ 1' để thoát khỏi tham chiếu nhóm chụp. – smci

2

Đây là cách tôi kết thúc khắc phục sự cố này. Tôi sử dụng hai regexes riêng biệt để phù hợp với nhóm chụp đầu tiên và thứ hai và chạy hai gregexpr cuộc gọi, sau đó kéo ra chuỗi con lần xuất hiện:

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 để có mã hoạt động. Tuy nhiên, tôi muốn chạy lệnh shell nhanh hơn từ R và sử dụng Bash một lớp lót như thế này 'expr' xyx0.0023xyxy ':' [^ 0-9] * \ ([. 0-9] \ + \) '' –

15

gsub() có thể làm điều này và trở lại chỉ nhóm chụp:

Tuy nhiên, để làm việc này, bạn phải chọn rõ ràng các phần tử bên ngoài nhóm chụp của bạn như được đề cập trong phần trợ giúp gsub().

(...) các phần tử của vectơ ký tự 'x' không được thay thế sẽ được trả về không thay đổi.

Vì vậy, nếu văn bản của bạn được chọn nằm ở giữa một số chuỗi, hãy thêm. * Trước và sau khi nhóm chụp sẽ cho phép bạn chỉ trả lại.

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

16

Hãy thử regmatches()regexec():

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

Cảm ơn giải pháp vanilla R và chỉ ra 'regmatches' mà tôi chưa từng thấy trước đây – Andy

3

Tôi thích biểu thức thông thường perl tương thích. Có lẽ ai đó không quá ...

Đây là một chức năng nào perl biểu thức thông thường tương thích và phù hợp với chức năng của các chức năng trong các ngôn ngữ khác mà tôi đang sử dụng để:

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

Như đã đề cập trong gói stringr , điều này có thể đạt được bằng cách sử dụng str_match() hoặc str_extract().

Phỏng theo hướng dẫn:

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})" 

Trích xuất và kết hợp nhóm của chúng tôi:

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

nhóm chỉ thị với một ma trận đầu ra (chúng tôi đang quan tâm đến cột 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

Giải pháp với strcapture từ 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 
Các vấn đề liên quan