Birden çok yakalama grubu ile R'de normal ifade grubu yakalama


95

R'de, normal ifade eşleşmesinden grup yakalamayı çıkarmak mümkün müdür? Bildiğim kadarıyla, hiçbiri söyleyebilirim grep, grepl, regexpr, gregexpr, sub, veya gsubgrup yakalar dönün.

Anahtar / değer çiftlerini şu şekilde kodlanmış dizelerden çıkarmam gerekiyor:

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

Her zaman çoklu tam eşleme grepleri yapabilirim veya bazı dış (R olmayan) işleme yapabilirim, ancak hepsini R içinde yapabileceğimi umuyordum. Bunu yapmak için böyle bir işlev sağlayan bir işlev veya paket var mı?

Yanıtlar:


120

str_match(), stringrpaketten bunu yapacak. Maçtaki her grup için bir sütunlu (ve tüm eşleşme için bir) karakter matrisi döndürür:

> 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
ve str_match_all()bir normal ifadedeki tüm grupları eşleştirmek için
smci

[, 1] için yalnızca yakalanan grupları nasıl yazdırabilirim?
nenur

Ne aradığından emin değilim. Yakalanan gruplar 2. ve 3. sütunlardır [,1]. Tam eşleşmedir. [,2:3]yakalanan gruplardır.
Kent Johnson

53

gsub bunu sizin örneğinizden yapar:

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

tırnak içindeki \ 'lerden iki kez kaçmanız gerekir, sonra bunlar normal ifade için çalışır.

Bu yardımcı olur umarım.


Aslında bir veri çerçevesine koymak için yakalanan alt dizeleri çıkarmam gerekiyor. Ancak cevabınıza baktığımda, istediğimi elde etmek için gsub ve birkaç strsplit'i zincirleyebilirim, belki: strsplit (strsplit (gsub (regex, "\\ 1 :: \\ 2 ::::", str ), "::::") [[1]], "::")
Daniel Dickison

9
Harika. R gsubkılavuz sayfasının, bir yakalama grubu referansından kaçmak için '\\ 1'e ihtiyacınız olduğunu gösteren bir örneğe çok ihtiyacı var.
smci

37

Deneyin regmatches()ve regexec():

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

4
Vanilya R çözümü ve regmatchesdaha önce hiç görmediğim bir şeyi işaret ettiğiniz için teşekkürler
Andy

Dizeyi neden iki kez yazmanız gerekiyor?
Stefano Borini

1
@StefanoBorini regexec, yalnızca eşleşmelerin konumuna ilişkin bilgileri tutan bir liste döndürür, bu nedenle regmatcheskullanıcının eşleşme listesinin ait olduğu dizeyi sağlamasını gerektirir.
RTbecard

19

gsub () bunu yapabilir ve yalnızca yakalama grubunu döndürebilir:

Ancak, bunun çalışması için gsub () yardımında belirtildiği gibi yakalama grubunuzun dışındaki öğeleri açıkça seçmeniz gerekir.

(...) ikame edilmeyen 'x' karakter vektörlerinin elemanları değişmeden döndürülecektir.

Bu nedenle, seçilecek metniniz bir dizenin ortasında yer alıyorsa, yakalama grubundan önce ve sonra. * Eklemek yalnızca onu döndürmenize izin vermelidir.

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


4

Perl uyumlu normal ifadeleri seviyorum. Muhtemelen başka biri de yapıyor ...

İşte perl uyumlu düzenli ifadeler yapan ve alışkın olduğum diğer dillerdeki işlevlerin işlevleriyle eşleşen bir işlev:

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

Bu, bu problemi çözme şeklimdir. Birinci ve ikinci yakalama gruplarını eşleştirmek için iki ayrı normal ifade kullandım ve iki gregexprçağrı çalıştırdım , ardından eşleşen alt dizeleri çıkardım:

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

Çalışan bir kod için +1. Bununla birlikte, R'den hızlı bir kabuk komutu çalıştırmayı ve bunun gibi bir Bash tek expr "xyx0.0023xyxy" : '[^0-9]*\([.0-9]\+\)'
satırlık

3

İle Çözüm strcapturedan 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

stringrPakette önerildiği gibi , bu, str_match()veya kullanılarak sağlanabilir str_extract().

Kılavuzdan uyarlanmıştır:

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

Gruplarımızı çıkarmak ve birleştirmek:

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

Çıktı matrisine sahip grupları belirtme (2+ sütunlarıyla ilgileniyoruz):

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"

peki ya
842566 4692

Eksikliği yakaladığınız için teşekkürler. _allİlgili stringrişlevler için son ek kullanılarak düzeltildi .
Megatron

0

Bu , seçilen cevaptan örnek alınarak paket çözücü kullanılarak yapılabilir :

# 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

Veya bir veri çerçevesinden başlayarak

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

İşlenmemiş normal ifadeyi, isteğe bağlı olarak adlandırılmış yakalama ile, tutarsız kalıbından alabilirsiniz:

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

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

Daha fazla bilgi: https://github.com/moodymudskipper/unglue/blob/master/README.md

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.