Tangkapan grup Regex di R dengan beberapa grup tangkapan


95

Di R, apakah mungkin untuk mengekstrak tangkapan grup dari pencocokan ekspresi reguler? Sejauh yang saya tahu, tidak ada grep, grepl, regexpr, gregexpr, sub, atau gsubmengembalikan menangkap kelompok.

Saya perlu mengekstrak pasangan nilai kunci dari string yang dikodekan sebagai berikut:

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

Saya selalu dapat melakukan beberapa grep pertandingan penuh, atau melakukan beberapa pemrosesan di luar (non-R), tetapi saya berharap saya dapat melakukan semuanya dalam R. Apakah ada fungsi atau paket yang menyediakan fungsi seperti itu untuk melakukan ini?

Jawaban:


119

str_match(), dari stringrpaket, akan melakukan ini. Ini mengembalikan matriks karakter dengan satu kolom untuk setiap grup dalam pertandingan (dan satu untuk seluruh pertandingan):

> 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
dan str_match_all()untuk mencocokkan semua grup dalam regex
smci

Bagaimana saya bisa mencetak hanya grup yang diambil untuk [, 1]?
nenur

Tidak yakin apa yang Anda cari. Grup yang ditangkap adalah kolom 2 & 3. [,1]adalah pertandingan penuh. [,2:3]adalah kelompok yang ditangkap.
Kent Johnson

52

gsub melakukan ini, dari contoh Anda:

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

Anda perlu meloloskan diri dua kali dari \ s dalam tanda kutip lalu berfungsi untuk regex.

Semoga ini membantu.


Sebenarnya saya perlu mengeluarkan substring yang diambil untuk dimasukkan ke dalam data.frame. Tapi, melihat jawaban Anda, saya kira saya bisa merangkai gsub dan beberapa strsplit untuk mendapatkan apa yang saya inginkan, mungkin: strsplit (strsplit (gsub (regex, "\\ 1 :: \\ 2 ::::", str ), "::::") [[1]], "::")
Daniel Dickison

9
Bagus. Halaman gsubmanual R sangat membutuhkan contoh yang menunjukkan bahwa Anda memerlukan '\\ 1' untuk keluar dari referensi grup tangkap.
smci

35

Coba regmatches()dan regexec():

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

4
Terima kasih untuk solusi vanilla R dan untuk petunjuk regmatchesyang belum pernah saya lihat sebelumnya
Andy

Mengapa Anda harus menulis string dua kali?
Stefano Borini

1
@StefanoBorini regexecmengembalikan daftar yang menyimpan informasi hanya mengenai lokasi pertandingan, oleh karena itu regmatchesmengharuskan pengguna untuk menyediakan string milik daftar pertandingan.
RTbecard

19

gsub () bisa melakukan ini dan hanya mengembalikan grup tangkapan:

Namun, agar ini berfungsi, Anda harus secara eksplisit memilih elemen di luar grup tangkapan Anda seperti yang disebutkan di bantuan gsub ().

(...) elemen vektor karakter 'x' yang tidak tersubstitusi akan dikembalikan tanpa perubahan.

Jadi jika teks Anda yang akan dipilih terletak di tengah-tengah beberapa string, menambahkan. * Sebelum dan sesudah grup pengambilan seharusnya memungkinkan Anda untuk hanya mengembalikannya.

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


4

Saya suka ekspresi reguler yang kompatibel dengan perl. Mungkin orang lain juga melakukannya ...

Berikut adalah fungsi yang kompatibel dengan perl dan cocok dengan fungsi fungsi dalam bahasa lain yang biasa saya gunakan:

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

Beginilah akhirnya saya mengatasi masalah ini. Saya menggunakan dua regex terpisah untuk mencocokkan grup tangkapan pertama dan kedua dan menjalankan dua gregexprpanggilan, lalu mengeluarkan substring yang cocok:

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 untuk kode kerja. Namun, saya lebih suka menjalankan perintah shell cepat dari R dan menggunakan Bash one-liner seperti iniexpr "xyx0.0023xyxy" : '[^0-9]*\([.0-9]\+\)'
Aleksandr Levchuk

3

Solusi dengan strcapturedari 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

Seperti yang disarankan dalam stringrpaket, ini dapat dicapai dengan menggunakan salah satu str_match()atau str_extract().

Diadaptasi dari manual:

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

Mengekstrak dan menggabungkan grup kami:

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

Grup penunjuk dengan matriks keluaran (kami tertarik pada kolom 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"

bagaimana dengan 842 566
4692

Terima kasih telah menangkap kelalaian. Dikoreksi menggunakan _allsufiks untuk stringrfungsi yang relevan .
Megatron

0

Ini bisa dilakukan dengan menggunakan package unglue , mengambil contoh dari jawaban yang dipilih:

# 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

Atau mulai dari bingkai data

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

Anda bisa mendapatkan regex mentah dari pola unglue, secara opsional dengan bernama capture:

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

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

Info lebih lanjut: https://github.com/moodymudskipper/unglue/blob/master/README.md

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.