Table of Content

  1. What is Regular Expression
  2. When to use RE
  3. How to use RE with re2r
  4. RE packages in R
  5. Benchmark

What is Regular Expression

It is a way to search for matches in strings. This is done by searching with “patterns” through the string.

You probably know the * and ? charachters used in the dir command on the command line. The * character means “zero or more arbitrary characters” and the ? means “one arbitrary character”.

When using a pattern like text?.*, it will find files like textf.txt, text1.R, and text9.Rmd.

This is exactly the way RE works, and RE supplies much more patterns.

When to use RE

Example usages could be:

  1. Remove all occurences of a specific tag from text file
  2. Check whether an e-mail address is well-formed

Basically we can do the following operations on a string with REs:

1. Test for a pattern

Search through a string for a pattern, and return boolean result or matched substrings.

2. Extract a substring

Search for a substring, and return that substring.

3. Replace a substring

Search for a substring that matches a pattern, and replace it by another string.

How to use RE with re2r

Here is a quick overview over the most common methods on how to execute a regular expression in re2r.

1. Search a string

re2_detect(string, pattern)

Searches the string expression for a pattern and returns boolean result.

re2_detect("this is just one test", "(o.e)")
## [1] TRUE

. stands for any character, possibly including newline . For more syntax, you can check out the RE2 Syntax vignette.

Here is an example of email pattern.

show_regex("\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b", width = 670, height = 280)
show_regex

show_regex

re2_detect("test@gmail.com", "\\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}\\b")
## [1] TRUE
re2_match(string, pattern)

This function will return the capture groups in ().

(res = re2_match("this is just one test", "(o.e)"))
##      .match .1   
## [1,] "one"  "one"
str(res)
##  're2_matrix' chr [1, 1:2] "one" "one"
##  - attr(*, "dimnames")=List of 2
##   ..$ : NULL
##   ..$ : chr [1:2] ".match" ".1"

The return result is a character matrix. .1 is the first capture group and it is unnamed group.

We can create named capture group with (?P<name>pattern) syntax.

(res = re2_match("this is just one test", "(?P<testname>this)( is)"))
##      .match    testname .2   
## [1,] "this is" "this"   " is"
str(res)
##  're2_matrix' chr [1, 1:3] "this is" "this" " is"
##  - attr(*, "dimnames")=List of 2
##   ..$ : NULL
##   ..$ : chr [1:3] ".match" "testname" ".2"

If there is no capture group, the matched origin strings will be returned.

test_string = c("this is just one test", "the second test");
(res = re2_match(test_string, "is"))
##      .match
## [1,] "is"  
## [2,] NA
str(res)
##  're2_matrix' chr [1:2, 1] "is" NA
##  - attr(*, "dimnames")=List of 2
##   ..$ : NULL
##   ..$ : chr ".match"

re2_match_all() will return the all of patterns in a string instead of just the first one.

re2_match_all(
            string = c("this is test", 
                      "this is test, and this is not test", 
                      "they are tests"), 
            pattern = "(?P<testname>this)( is)")
## [[1]]
##      .match    testname .2   
## [1,] "this is" "this"   " is"
## 
## [[2]]
##      .match    testname .2   
## [1,] "this is" "this"   " is"
## [2,] "this is" "this"   " is"
## 
## [[3]]
##      .match testname .2

2. Replace a substring

re2_replace(string, pattern, rewrite)

Searches the string “input string” for the occurence(s) of a substring that matches ‘pattern’ and replaces the found substrings with “rewrite text”.

input_string = "this is just one test";
new_string = "my"
re2_replace(input_string, "(o.e)", new_string)
## [1] "this is just my test"

3. Extract a substring

re2_extract(input, pattern)

Searches the string “input string” for the occurence(s) of a substring that matches ‘pattern’ and return the found substrings with “rewrite text”.

re2_extract("yabba dabba doo", "yabba")
## [1] "yabba"
re2_extract("test@me.com", "(.*)@([^.]*)")
## [1] "test@me"

4. Pre-compiled RE

We can create a regular expression object (RE2 object) from a string. It will reduce the time to parse the syntax of the same pattern.

And this will also give us more option for the pattern. run help(re2) to get more detials.

regexp = re2("test", case_sensitive = FALSE)
print(regexp)
## re2 pre-compiled regular expression
## 
## pattern: test
## number of capturing subpatterns: 0
## capturing names with indices: 
## .match
## expression size: 10

5. Multithread

Use parallel option to enable multithread feature. It will improve performance for large inputs with a multi core CPU.

re2_match(string, pattern, parallel = T)

RE packages in R

1. Base R with PCRE

Base R functions such as regexpr use PCRE when given the perl = TRUE argument. PCRE includes many useful features, such as named capture, but has an exponential time complexity.

2. Base R with TRE

Base R functions such as regexpr use TRE when given the perl = FALSE argument. TRE has a polynomial time complexity but does not include named capture groups.

3. stringi with ICU

stringr::str_match and stringi::stri_match use the regex engine from the ICU library, which has an exponential time complexity. The stringi package does not support named capture yet as such a feature set is still considered as experimental in ICU.

4. re2r with RE2

RE2 is a primarily DFA based regexp engine from Google that is very fast at matching large amounts of text. It is has a polynomial time complexity (or fast and scalable in general case), but it does not support look behind and some regular expression features.

Above all

Although being slightly different to use (because of the design of the engines), all are quite similar to Perl’s implementation of REs.

Benchmarks

Benchmarks are disabled by default for CRAN. See https://qinwenfeng.com/re2r_doc for the results by Travis-CI.