Passing function to R regex-based tools

Viewed 49

This string manipulation problem has evaded my best efforts. I have a string, e.g.

eg_str="[probability space](posts/probability space.md) is ... [Sigma Field](posts/Sigma Field.md)"

for which I would like to replace all spaces in the wildcard for ([wildcard].md) with underscores. My first thought was to use either gsub or stringr's str_replace_all to pass the appropriate substrings to a simple function. Something like

convert_space_to_underscore<-function(string){
    return(str_replace(string," ","_"))
}
normal_eg_str<-gsub("\\((.+?)md\\)",paste0("(",convert_space_to_underscore("\\1"),"md)"),normal_eg_str)

or

normal_eg_str<-str_replace_all(document,"\\((.+?)md\\)",paste0("(",convert_space_to_underscore("\\1"),".md)"))

When I run these however, it appears that the argument to convert_space_to_underscore is being passed, rather than the output, because the string returns unchanged (if you make an error in the paste0 component, say have paste0("(",convert_space_to_underscore("\\1"),".m)"), then the string returns as

eg_str="[probability space](posts/probability space.m) is ... [Sigma Field](posts/Sigma Field.m)"

so I'm quite sure that what is happening is that str_replace_all and gsub are simply not evaluating the function).

Is there a way to force evaluation? This would be most ideal, as it would allow for the regex component to remain somewhat readable. However, I would welcome any pure-regex solutions as well — my attempts have all lead to greedy errors, no matter where I seem to sprinkle ? and {0} special characters. (Word of caution: there will be some matching substrings with more than one space e.g. [Dynklin's Pi Lambda](posts/dynklins pi lambda.md))

1 Answers

You can use

library(stringr)
eg_str <- "[probability space](posts/probability space.md) is ... [Sigma Field](posts/Sigma Field.md)"
str_replace_all(eg_str, "\\([^()]+\\.md\\)", function(x) gsub(" ", "_", x, fixed=TRUE) )
## => [1] "[probability space](posts/probability_space.md) is ... [Sigma Field](posts/Sigma_Field.md)"

See online R demo.

NOTE: To replace one or more whitespace chunks with a single underscore, you will need a regex in gsub: gsub("\\s+", "_", x).

The first regex finds all strings that

  • \( - start with (
  • [^()]+ - have one or more chars other than ( and )
  • \.md - a .md string
  • \) - and end with )

Then, the match is passed to an anonymous function that replaced each regular space with a _ (with gsub(" ", "_", x, fixed=TRUE)).

A base R solution (less readable, but using a plain regex):

eg_str <- "[probability space](posts/probability space.md) is ... [Sigma Field](posts/Sigma Field.md)"
gsub("(?:\\G(?!^)|\\()[^()\\s]*\\K\\s+(?=[^()]*\\.md\\))", "_", eg_str, perl=TRUE)

See this R demo online. See this regex demo. Details:

  • (?:\G(?!^)|\() - end of the preceding match or a ( char
  • [^()\s]* - any 0 or more chars other than (, ) and whitespace
  • \K - match reset operator that discards all text matched so far from the overall match memory buffer
  • \s+ - one or more whitespaces
  • (?=[^()]*\.md\)) - there should be zero or more chars other than ( and ) followed with .md) immediately to the right of the current location.
Related