在连续的字符运行中删除重复项

4

我有包含大量重复内容的字符串,就像这样:

tst <- c("C>C>C>B>B>B>B>C>C>*>*>*>*>*>C", "A>A>A", "*>B>B", 
     "A>A>A>A>A>*>A>A>A>*>*>*>*>A>A", "*>C>C", "A")

我希望删除所有连续的大写字母和“*”字符,因此预期结果是这样的:
[1] "CBC*C" "A"     "*B"    "A*A*A" "*C"    "A"

我已成功提取了重复的大写字母:
library(stringr)
unlist(str_extract_all(gsub(">", "", tst), "(.)(?=\\1)"))
[1] "C" "C" "B" "B" "B" "C" "*" "*" "*" "*"

但是我在这里有些卡住了。我的直觉是函数which可能有帮助,它返回索引,但不知道如何在这种情况下实现。

有什么想法吗?

编辑:

我自己距离解决方案并不远 - 只需使用负向先行断言(而不是正向先行断言)就可以解决问题:

str_extract_all(gsub(">", "", tst), "(.)(?!\\1)")
[[1]]
[1] "C" "B" "C" "*" "C"

[[2]]
[1] "A"

[[3]]
[1] "*" "B"

[[4]]
[1] "A" "*" "A" "*" "A"

[[5]]
[1] "*" "C"

[[6]]
[1] "A"
4个回答

3
我们可以使用gsub
gsub("([A-Z*]>)\\1+", "\\1", tst)
#[1] "C>B>C>*>C"

为了得到第二个结果,删除>标签。
gsub(">", "", gsub("([A-Z*]\\>)\\1+", "\\1", tst) ,fixed = TRUE)
#[1] "CBC*C"

根据下面评论中 OP 的说法,可能是

gsub("(.)\\1+", "\\1", gsub(">", "", tst))
#[1] "CBC*C"
gsub("(.)\\1+", "\\1", gsub(">", "", "A>"))
#[1] "A"
gsub("(.)\\1+", "\\1", gsub(">", "", "A>A"))
#[1] "A"
gsub("(.)\\1+", "\\1", gsub(">", "", "A>A>A>A"))
#[1] "A"

@ChrisRuehlemann 那个的预期是什么? - akrun
只有一个字母 A,仅此而已。 - Chris Ruehlemann
@ChrisRuehlemann 这段代码返回 gsub("(.)\\1+", "\\1", gsub(">", "", "A>A>A>A"))# [1] "A",在 gsub("(.)\\1+", "\\1", gsub(">", "", tst))# [1] "CBC*C" 上进行测试。 - akrun
@ChrisRuehlemann 这也适用于 gsub("(.)\\1+", "\\1", gsub(">", "", "A>A"))# [1] "A" 相比之下,gsub("((.)>)\\1+\\2*", "\\2", "A>A")# [1] "A>A" - akrun

3

另一种获得CBC*C的方式可能是使用 2 组并在替换中使用第 2 组。

((.)>)\1+

正则表达式演示

例子

tst <- "C>C>C>B>B>B>B>C>C>*>*>*>*>*>C"
gsub("((.)>)\\1+", "\\2", tst)

输出

[1] "CBC*C"

1
这是一个优美的解决方案。 - Chris Ruehlemann
为什么这个解决方案在类似 gsub("((.)>)\\1+", "\\2", "A>A>A>A") 的情况下失败了?如何编辑它以使其适用于这种类型的情况? - Chris Ruehlemann
@ChrisRuehlemann 这取决于预期的结果是什么。当前模式重复捕获组以获取连续部分。替换使用内部第二个组。 - The fourth bird
@ChrisRuehlemann 结果确实是 A。请注意,它也适用于 >>>>>>>A,因为您首先删除所有的 >,然后将剩余的连续 A 替换为单个 A。您可以将其简化为一个捕获组 gsub("(.)\\1+", "\\1", gsub(">", "", tst)) - The fourth bird
1
非常感谢。事实证明,最好的解决方案确实是 gsub("(.)\\1+", "\\1", gsub(">", "", tst))。@akrun 在几分钟前发布了这个答案。所以为了公平起见,我将接受他的答案。 - Chris Ruehlemann
显示剩余3条评论

2

对于我们对正则表达式过敏的人:

paste(rle(strsplit(tst, ">")[[1]])$values, collapse = ">") # or collapse = ""
[1] "C>B>C>*>C"

当然,对于包含一串小写字母的字符串,例如"A>A>a>a>A>A",此方法将失败。


1

一个没有正则表达式的相对通用的 base R 方法。

这里的思路是将字符串融合成一组,然后依次删除重复的模式(这使它与 unique 不同):

tst <- "C>C>C>B>B>B>B>C>C>*>*>*>*>*>C"
st <- paste(unlist(strsplit(tst,">")),collapse="")
#[1] "CCCBBBBCC*****C"

paste( unlist( sapply( 1:nchar(st), function(x){
  if( substr(st,x,x) != substr(st,(x+1),(x+1)) ){ substr(st,x,x) } } ) ), collapse="" )
#[1] "CBC*C"

如果您想要小写功能(不包括删除小写字母),请使用以下内容:

paste( unlist( sapply( 1:nchar(st), function(x){
  a=substr(st,x,x); b=substr(st,(x+1),(x+1));
  if( a != b & toupper(a) == a ){ a } else if( toupper(a) != a ){ a }  } ) ), collapse="" )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接