Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do str_extract with base R?

Tags:

regex

r

I am balancing several versions of R and want to change my R libraries loaded depending on which R and which operating system I'm using. As such, I want to stick with base R functions.

I was reading this page to see what the base R equivalent to stringr::str_extract was:

http://stat545.com/block022_regular-expression.html

It suggested I could replicate this functionality with grep. However, I haven't been able to get grep to do more than return the whole string if there is a match. Is this possible with grep alone, or do I need to combine it with another function? In my case I'm trying to distinguish between CentOS versions 6 and 7.

grep(pattern = "release ([0-9]+)", x = readLines("/etc/system-release"), value = TRUE)
like image 729
wdkrnls Avatar asked Dec 06 '17 16:12

wdkrnls


3 Answers

1) strcapture If you want to extract a string of digits and dots from "release 1.2.3" using base then

x <- "release 1.2.3"
strcapture("([0-9.]+)", x, data.frame(version = character(0)))
##   version
## 1   1.2.3

2) regexec/regmatches There is also regmatches and regexec but that has already been covered in another answer.

3) sub Also it is often possible to use sub:

sub(".* ([0-9.]+).*", "\\1", x)
## [1] "1.2.3"

3a) If you know the match is at the beginning or end then delete everything after or before it:

sub(".* ", "", x)
## [1] "1.2.3"

4) gsub Sometimes we know that the field to be extracted has certain characters and they do not appear elsewhere. In that case simply delete every occurrence of every character that cannot be in the string:

gsub("[^0-9.]", "", x)
## [1] "1.2.3"

5) read.table One can often decompose the input into fields and then pick off the desired one by number or via grep. strsplit, read.table or scan can be used:

read.table(text = x, as.is = TRUE)[[2]]
## [1] "1.2.3"

5a) grep/scan

grep("^[0-9.]+$", scan(textConnection(x), what = "", quiet = TRUE), value = TRUE)
## [1] "1.2.3"

5b) grep/strsplit

grep("^[0-9.]+$", strsplit(x, " ")[[1]], value = TRUE)
## [1] "1.2.3"

6) substring If we know the character position of the field we can use substring like this:

substring(x, 9)
## [1] "1.2.3"

6a) substring/regexpr or we may be able to use regexpr to locate the character position for us:

substring(x, regexpr("\\d", x))
## [1] "1.2.3"

7) read.dcf Sometimes it is possible to convert the input to dcf form in which case it can be read with read.dcf. Such data is of the form name: value

 read.dcf(textConnection(sub(" ", ": ", x)))
 ##      release
 ## [1,] "1.2.3"
like image 111
G. Grothendieck Avatar answered Nov 03 '22 23:11

G. Grothendieck


You could do

txt <- c("foo release 123", "bar release", "foo release 123 bar release 123")
pattern <- "release ([0-9]+)"
stringr::str_extract(txt, pattern)
# [1] "release 123" NA            "release 123"
sapply(regmatches(txt, regexec(pattern, txt)), "[", 1)
# [1] "release 123" NA            "release 123"
like image 31
lukeA Avatar answered Nov 04 '22 00:11

lukeA


txt <- c("foo release 123", "bar release", "foo release 123 bar release 123")
pattern <- "release ([0-9]+)"

Extract first match

sapply(
    X = txt,
    FUN = function(x){
        tmp = regexpr(pattern, x)
        m = attr(tmp, "match.length")
        st = unlist(tmp)
        if (st == -1){NA}else{substr(x, start = st, stop = st + m - 1)}
    },
    USE.NAMES = FALSE)
#[1] "release 123" NA            "release 123"

Extract all matches

sapply(
    X = txt,
    FUN = function(x){
        tmp = gregexpr(pattern, x)
        m = attr(tmp[[1]], "match.length")
        st = unlist(tmp)
        if (st[1] == -1){
            NA
        }else{
                sapply(seq_along(st), function(i) substr(x, st[i], st[i] + m[i] - 1))
            }
    },
    USE.NAMES = FALSE)
#[[1]]
#[1] "release 123"

#[[2]]
#[1] NA

#[[3]]
#[1] "release 123" "release 123"
like image 23
d.b Avatar answered Nov 04 '22 01:11

d.b