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?

Daniel Dickison
sumber

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"    
Kent Johnson
sumber
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.

David Lawrence Miller
sumber
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"
cemburu
sumber
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"

kaso
sumber
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
}
ruffbytes
sumber
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"))
Daniel Dickison
sumber
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
Artem Klevtsov
sumber
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"
Megatron
sumber
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

Moody_Mudskipper
sumber