R stringr unexpected behavior with str_replace & str_pad. Bug or Layer-8 problem?

Viewed 38

I am using R 4.1.3 and the stringr-package 1.4.0 and get some unexpected results from this code:

stringr::str_replace(string = "5", 
                     pattern = "([0-9]+)", 
                     replacement = stringr::str_pad(string = "\\1", width = 3,  side = "left",  pad = "0"))

Expected: "005"; Result: "05".

All the parts generate the expected results:

(1) The padding

stringr::str_pad(string = "5", width = 3, side = "left", pad = "0")

Returns "005"

(2) The regex match

stringr::str_replace(string = "5", pattern = "([0-9]+)", replacement = "\\1")

Returns "5".

Only the combination of these two leads to unexpected behavior.

For clarification, I already have working code and several solutions to choose from to achive what I want to do, i.e. using an anonymous function:

stringr::str_replace(string = "5", 
                     pattern = "([0-9]+)", 
                     replacement = {\(x) stringr::str_pad(string = x, width = 3,  side = "left",  pad = "0")})

The intention of the post is to clarifiy why the code at the top does not work.

Thanks in advance for any helpful input.

Edit: It seems that "\1" refers to the content of the capture group, but the character length is determined from the literal "\1".

stringr::str_replace(string = "5", 
                     pattern = "([0-9]+)", 
                     replacement = {\(x) as.character(nchar(x))})

stringr::str_replace(string = "5", 
                     pattern = "([0-9]+)", 
                     replacement = as.character(nchar("\\1")))

Returns "1" and "2". The second example always returns "2" as replacement for the captured group, independend of its content.

1 Answers

The problem here is that \1 in the scope of the inner call to str_pad() does not mean the first capture group, but rather the number 1 escaped by backslash. Instead, consider this version as a workaround:

x <- c("5", "12", "345", "1234")
output <- sub("^0{1,3}(\\d{3,})", "\\1", paste0("000", x))
output

[1] "005"  "012"  "345"  "1234"
Related