splitting a string with regex in R

Viewed 59

I'm trying to split strings based on specific patterns. I have data nested within curly brackets. What I'm trying to do is split the string at the double curly bracket. I've figured out how to do this with "separate" within a data frame, but for future reference I'd love to know why this doesn't work.

I've provided an example below on a single string:

pattern_test<-"[^\\}{2,2}]*\\}{2,2}"
teststring <- "{the {dog} is {hot}},{the {cat} is {lazy}}"
tmp<-unlist(str_extract_all(teststring, pattern_test))
tmp

tmp evaluates to ("hot}}", "lazy}}").

In words, what I'm trying to do in "pattern_test" is to define a class that includes all characters that are not exactly "}}": [^\\}{2,2}] and find as many characters in that class: *, followed by "}}" (outside the square brackets: \\}{2,2}). I suspect I'm making a fundamental error but most of the examples I've found online haven't helped me figure out what the error is. What I want tmp to evaluate to is:

("{the {dog} is {hot}}", ",{the {cat} is {lazy}}"). Why is the substring cutting off at the open bracket?

1 Answers

The problem is that you cannot match any text but a certain multichar substring with a negated character class, as character classes are meant to match single characters as separate chars, not as sequences of chars.

Another issue is that you are trying to match a recursive pattern, and stringr / stringi package is using the ICU regex library that does not support recursion in regex.

To match what you want, you can only use PCRE regex library with R:

pattern_test<-"\\{(?:[^{}]++|(?R))*}"
teststring <- "{the {dog} is {hot}},{the {cat} is {lazy}}"
unlist(regmatches(teststring, gregexpr(pattern_test, teststring, perl=TRUE)))
## => [1] "{the {dog} is {hot}}"  "{the {cat} is {lazy}}"

See the R demo online. That is:

  • \{ - match a {
  • (?:[^{}]++|(?R))* - zero or more occurrences of one or more chars other than { and } or the whole regex pattern (recursed)
  • } - a } char.
Related