Capture from 1st occurance of x to nth occurance of y

Viewed 48

I have a regular expression problem that seems fairly do-able, but i can't seem to be able to get correct.

Give strings that look like:

id1234|a; b; c; d
id5678|a; b; e; f
id9012|a; g; h; i

I'm trying to capture from the pipe to a fixed occurrence of the semicolon, intuitively I'd expect something like:

"(?<=\\|)(([^;]*;){1}[^;]*).*"

to select from the pipe up to the 2nd semicolon, since:

"^(([^;]*;){1}[^;]*).*"

selects from the beginning of the line to the 2nd semicolon.

https://regexr.com/

believes that:

(?<=\|)(([^;]*;){1}[^;]*).*

Correctly selects from the pipe onward, but appears to fail to end the capture at the correct semicolon.

But in R gsub complains:

a <- c("id1234|a; b; c; d",
       "id5678|a; b; e; f",
       "id9012|a; g; h; i",
       "id3456|a; j; k; l")

b <- gsub(pattern = "^(([^;]*;){1}[^;]*).*",
          replacement = "\\1",
          x = a)
b
[1] "id1234|a; b" "id5678|a; b" "id9012|a; g" "id3456|a; j"

c <- gsub(pattern = "(?<=\\|)(([^;]*;){1}[^;]*).*",
          replacement = "\\1",
          x = a)
Error in gsub(pattern = "(?<=\\|)(([^;]*;){1}[^;]*).*", replacement = "\\1",  : 
  invalid regular expression '(?<=\|)(([^;]*;){1}[^;]*).*', reason 'Invalid regexp'
In addition: Warning message:
In gsub(pattern = "(?<=\\|)(([^;]*;){1}[^;]*).*", replacement = "\\1",  :
  TRE pattern compilation error 'Invalid regexp'
1 Answers

You get the error because you are using a lookbehind, (?<=\|), but did not use the perl=TRUE argument to enable the PCRE regex engine that supports lookbehinds. The default TRE regex engine does not support lookarounds.

You need to match the whole string:

sub("^[^|]*\\|([^;]*;[^;]*).*", "\\1", a)

See the regex demo and the R demo:

a <- c("id1234|a; b; c; d",
       "id5678|a; b; e; f",
       "id9012|a; g; h; i",
       "id3456|a; j; k; l")
sub("^[^|]*\\|([^;]*;[^;]*).*", "\\1", a)
## => [1] "a; b" "a; b" "a; g" "a; j"

Regex details:

  • ^ - string start
  • [^|]* - zero or more chars other than |
  • \| - a | char
  • ([^;]*;[^;]*) - Group 1: any zero or more chars other than ;, ; and again any zero or more chars other than ;
  • .* - the rest of the string.

If you want to extract, you should actually use the extracting regex methods, for example:

a <- c("id1234|a; b; c; d", "id5678|a; b; e; f", "id9012|a; g; h; i", "id3456|a; j; k; l")
unlist(regmatches(a, gregexpr("(?<=\\|)[^;]*;[^;]*", a, perl=TRUE)))
## => [1] "a; b" "a; b" "a; g" "a; j"

library(stringr)
str_extract(a, "(?<=\\|)[^;]*;[^;]*")
## => [1] "a; b" "a; b" "a; g" "a; j"
Related