【发布时间】:2020-03-05 14:41:50
【问题描述】:
我希望能够仅在给定的分隔符集之外使用grepl() 和gsub(),例如我希望能够忽略引号之间的文本。
这是我想要的输出:
grepl2("banana", "'banana' banana \"banana\"", escaped =c('""', "''"))
#> [1] TRUE
grepl2("banana", "'banana' apple \"banana\"", escaped =c('""', "''"))
#> [1] FALSE
grepl2("banana", "{banana} banana {banana}", escaped = "{}")
#> [1] TRUE
grepl2("banana", "{banana} apple {banana}", escaped = "{}")
#> [1] FALSE
gsub2("banana", "potatoe", "'banana' banana \"banana\"")
#> [1] "'banana' potatoe \"banana\""
gsub2("banana", "potatoe", "'banana' apple \"banana\"")
#> [1] "'banana' apple \"banana\""
gsub2("banana", "potatoe", "{banana} banana {banana}", escaped = "{}")
#> [1] "{banana} potatoe {banana}"
gsub2("banana", "potatoe", "{banana} apple {banana}", escaped = "{}")
#> [1] "{banana} apple {banana}"
实际案例可能以不同的数量和顺序引用了子字符串。
我编写了以下函数来处理这些情况,但是它们很笨重,而且gsub2() 一点也不健壮,因为它临时用占位符替换了分隔的内容,并且这些占位符可能会受到后续操作的影响。
regex_escape <-
function(string,n = 1) {
for(i in seq_len(n)){
string <- gsub("([][{}().+*^$|\\?])", "\\\\\\1", string)
}
string
}
grepl2 <-
function(pattern, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE,
useBytes = FALSE, escaped =c('""', "''")){
escaped <- strsplit(escaped,"")
# TODO check that "escaped" delimiters are balanced and don't cross each other
for(i in 1:length(escaped)){
close <- regex_escape(escaped[[i]][[2]])
open <- regex_escape(escaped[[i]][[1]])
pattern_i <- sprintf("%s.*?%s", open, close)
x <- gsub(pattern_i,"",x)
}
grepl(pattern, x, ignore.case, perl, fixed, useBytes)
}
gsub2 <- function(pattern, replacement, x, ignore.case = FALSE, perl = FALSE,
fixed = FALSE, useBytes = FALSE, escaped =c('""', "''")){
escaped <- strsplit(escaped,"")
# TODO check that "escaped" delimiters are balanced and don't cross each other
matches <- character()
for(i in 1:length(escaped)){
close <- regex_escape(escaped[[i]][[2]])
open <- regex_escape(escaped[[i]][[1]])
pattern_i <- sprintf("%s.*?%s", open, close)
ind <- gregexpr(pattern_i,x)
matches_i <- regmatches(x, ind)[[1]]
regmatches(x, ind)[[1]] <- paste0("((",length(matches) + seq_along(matches_i),"))")
matches <- c(matches, matches_i)
}
x <- gsub(pattern, replacement, x, ignore.case, perl, fixed, useBytes)
for(i in seq_along(matches)){
pattern <- sprintf("\\(\\(%s\\)\\)", i)
x <- gsub(pattern, matches[[i]], x)
}
x
}
有没有使用正则表达式且没有占位符的解决方案?请注意,我当前的函数支持多对分隔符,但我会满足于仅支持一对分隔符的解决方案,并且不会尝试匹配简单引号之间的子字符串。
也可以使用不同的分隔符,例如 { 和 } 而不是 2 " 或 2 ',如果有帮助的话。
我也可以强加perl = TRUE
【问题讨论】:
-
你可以使用
perl = TRUE吗? -
是的,绝对的