How to replace `qux$foo$bar` with `qux[["foo"]][["bar]]`? (dollar subsetting to brackets subsetting)

Viewed 53

In a R script (that I would read with readLines), I want to replace every occurence of qux$foo$bar with qux[["foo"]][["bar]]. But I'm not a regex master.

I started with this regex:

> gsub("(\\w*)(\\$)(\\w*)", '\\1[["\\3"]]', "qux$foo$bar; input$test$a$a") %>% cat
qux[["foo"]][["bar"]]; input[["test"]][["a"]][["a"]]

Nice. But I also want to handle the case of backticks. So I tried:

> gsub("(\\w*)(\\$)`{0,1}(\\w*)`{0,1}", '\\1[["\\3"]]', "qux$`foo`; bar$`baz`; x$uvw") %>% cat
qux[["foo"]]; bar[["baz"]]; x[["uvw"]]

Looks correct. But between the backticks, there could be a space, and the previous way does not work in this case. So I tried the following, which neither does not work:

gsub("(\\w*)(\\$)`{0,1}(.*)`{0,1}", '\\1[["\\3"]]', "qux$`fo o`") %>% cat
qux[["fo o`"]]

Could you help to find the right regex pattern? It seems that instead of \\w I need something which means match a "word that can contain spaces".

2 Answers

You might repeat optional spaces before and after matching 1 or more word characters.

You don't need a capture group for the $ but instead you could use a capture group to pair up the backtick in case it is there or not using a backreference to group 2.

To repeat 0+ whitespace chars you can also use \s but that could also match a newline.

Note that \w* matches optional word chars, and {0,1} can be written as ?

(\w*)\$(`?)( *\w+(?: +\w+)* *)\2

The pattern matches:

  • (\w*) Capture group 1 Match optional word characters
  • \$ Match $
  • (`?) Capture group 2, optionally match a backtick
  • ( *\w+(?: +\w+)* *) Capture group 3 Match repetitions of word characters between spaces
  • \2 Backreference to what is captured in group 2 (yes or no backtick)

Regex demo

gsub("(\\w*)\\$(`?)( *\\w+(?: +\\w+)* *)\\2", '\\1[["\\3"]]', "qux$fo o$bar", perl=TRUE)

Output

[1] "qux[[\"fo o\"]][[\"bar\"]]"

You can use

gsub('(\\w*)(?|\\$`([^`]*)`|\\$([^\\s$]+))', '\\1[["\\2"]]', x, perl=TRUE)
## Or
gsub('\\$`([^`]*)`|\\$([^\\s$]+)', '[["\\1\\2"]]', x, perl=TRUE)

See the regex #1 demo and regex #2 demo. Details:

  • (\w*) - Group 1 (\1): zero or more word chars
  • (?|$`([^`]*)`|$([^\s$]+)) - a branch reset group matching either
    • $`([^`]*)` - $, backtick, Group 2 (\2) capturing zero or more non-backtick chars, and a backtick.
    • | - or
    • $([^\s$]+) - $, then Group 2 (\2) capturing one or more chars other than whitespace and $

See the R demo:

x <- c('qux$foo$bar','qux$foo$bar; input$test$a$a','qux$`foo`; bar$`baz`; x$uvw','qux$`fo o`', 'q_ux$f_o_o$b.a_r')
gsub('(\\w*)(?|\\$`([^`]*)`|\\$([^\\s$]+))', '\\1[["\\2"]]', x, perl=TRUE)
## Or
## gsub('\\$`([^`]*)`|\\$([^\\s$]+)', '[["\\1\\2"]]', x, perl=TRUE)

Output:

[1] "qux[[\"foo\"]][[\"bar\"]]"                                     
[2] "qux[[\"foo\"]][[\"bar;\"]] input[[\"test\"]][[\"a\"]][[\"a\"]]"
[3] "qux[[\"foo\"]]; bar[[\"baz\"]]; x[[\"uvw\"]]"                  
[4] "qux[[\"fo o\"]]"                                               
[5] "q_ux[[\"f_o_o\"]][[\"b.a_r\"]]"  

Note: backslashes in the output are console artifacts to keep the double quoted strings valid string literals, they are not part of the plain text output.

Related