[英]Insertion of characters in strings in R
我想在字母(大写和小写)之间插入“ &
”,但不要在字母之前或之后插入,并将每个小写字母x
替换为tt$X==0
,每个大写字母X
由tt$X==1
,每个+
by )|(
,加上一个左括号和整个字符串的右括号,以便得到一个可以在R中计算的表达式。例如,我有字符串
st <- "AbC + de + FGHIJ"
结果应如下所示:
"(tt$A==1 & tt$B==0 & tt$C==1) | (tt$D==0 & tt$E==0) | (tt$F==1 & tt$G==1 & tt$H==1 & tt$I==1 & tt$J==1)"
我可以使用gsub()
函数轻松完成吗?
你可以做到这一点,但它不是很优雅
st <- "AbC + de + FGHIJ"
t1 <- gsub("([a-z])", "tt\\$\\U\\1==0", st, perl = TRUE)
t2 <- gsub("((?<!\\$)[A-Z])", "tt\\$\\U\\1==1", t1, perl = TRUE)
t3 <- gsub("([0-9])(tt)", "\\1 & \\2", t2)
t4 <- gsub(" + ", ") | (", t3, fixed = TRUE)
t5 <- paste("(", t4, ")", sep = "")
st
# "AbC + de + FGHIJ"
t5
# "(tt$A==1 & tt$B==0 & tt$C==1) | (tt$D==0 & tt$E==0) | (tt$F==1 & tt$G==1 & tt$H==1 & tt$I==1 & tt$J==1)"
这是对它的作用的解释:
t1用tt$X==0
替换所有小写字母,其中X
是被替换的大写字母。 使用\\\\U\\\\1
生成大写字母,其中\\\\U
生成大写, \\\\1
返回第一个捕获组。 捕获组是括号内捕获的组。
既然小写字母不在路上(必须首先执行以便我们不替换tt
),我们会替换大写字母,但前提是它们前面都是$
。 为了告诉gsub
在美元符号后忽略大写字母,我们使用负后看(?<!)
和\\\\$
告诉它忽略美元符号。 然后我们再用我们要替换的大写字母替换我们的字母。
接下来,我们需要在我们替换的所有字母之间插入一个空格。 最好的方法是只需要确认每次需要空格时tt$
前面都会有一个数字。 因此,我们查找一个数字后跟“tt”,并将其替换为第一个捕获组“&”,然后是第二个捕获组。
然后我们需要替换“+”符号。 所以我们用“)|(”。我们使用fixed = TRUE
来替换它和周围的空格,以避免需要转义括号和OR运算符。
最后,我们附加前导括号和尾随括号,以便为我们提供一个功能完备的条件短语。
根据其他解决方案中的评论,我们可以对我提出的解决方案进行一些更改,以便a)使其更加健壮,并且b)更灵活。 为了使其更加健壮,我们只需更改t4
即可:
t4 <- gsub(" ?\\\\+ ?", ") | (", t3)
我们只是在空格后面添加问号,说可以有0或1,转义+
,并删除fixed = TRUE
。 我们必须删除fixed = TRUE
因为我们需要正则表达式函数来测试空间。
为了使其更加灵活,我们只需将其包装在一个允许我们传递字符串和所需对象名称的函数中。
parse_string <- function(string, object_name) {
st <- string
t1 <- gsub("([a-z])", paste0(object_name, "\\$\\U\\1==0"), st, perl = TRUE)
t2 <- gsub("((?<!\\$)[A-Z])", paste0(object_name, "\\$\\U\\1==1"), t1, perl = TRUE)
t3 <- gsub(paste0("([0-9])(", object_name, ")"), "\\1 & \\2", t2)
t4 <- gsub(" ?\\+ ?", ") | (", t3)
t5 <- paste("(", t4, ")", sep = "")
return(t5)
}
> parse_string(st, "tt") == t5
# [1] TRUE
> parse_string(st, "foo")
# [1] "(foo$A==1 & foo$B==0 & foo$C==1) | (foo$D==0 & foo$E==0) | (foo$F==1 & foo$G==1 & foo$H==1 & foo$I==1 & foo$J==1)"
> parse_string("AbC+de+FGHIJ", "tt") == t5
# [1] TRUE
一堆正则表达式很少优雅,而且通常难以调试。 如果元素之间没有精确的间距,则上述正则表达式解决方案会失败。
> tt("aBc+b")
[1] "(tt$A==0 & tt$B==1 & tt$C==0+tt$B==0)"
> tt("aBc + b")
[1] "(tt$A==0 & tt$B==1 & tt$C==0) | (tt$B==0)"
有时您只需要自己拆分并处理它们。 这是一个解决方案:
doChar = Vectorize(
function(c){
sprintf("tt$%s==%s",toupper(c),ifelse(c %in% LETTERS,"1","0"))
}
)
doWord = Vectorize(function(W){
cs = strsplit(W,"")[[1]]
paste0("(",
paste(doChar(cs),collapse=" & "),
")")
})
processString = function(st){
parts = strsplit(st,"\\+")[[1]]
parts = gsub(" ","",parts)
paste0(doWord(parts),collapse=" | ")
}
可能有很多方法可以让它变得更好,但它的好处是更容易调试(你可以测试部件)并且看起来不像线路噪音:)
对于给定的示例字符串,它返回与tt
函数相同的函数,它是regexp解决方案的函数包装器:
> tt(st)==processString(st)
[1] TRUE
但处理间距:
> processString("aBc + deF") == processString("aBc+deF")
[1] TRUE
编写在它接受的输入中有点灵活的代码总是一个好主意。 您可能还注意到输出元素的tt
部分只出现一次,因此如果要输出foo$A
而不是tt$A
,则只需要进行一次更改。 正则表达式解决方案在三个地方有这个(如果我错过了一个,可能有四个!)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.