正则表达式匹配模式的每第 n 次出现

Regex expression to match every nth occurence of a pattern

考虑这个字符串,

str = "abc-de-fghi-j-k-lm-n-o-p-qrst-u-vw-x-yz"

我想在每第 n 次出现模式时分隔字符串,这里 -:

f(str, n = 2)
[1] "abc-de" "fghi-j" "k-lm" "n-o"...

f(str, n = 3)
[1] "abc-de-fghi" "j-k-lm" "n-o-p" "qrst-u-vw"...

我知道我可以这样做:

spl <- str_split(str, "-", )[[1]]
unname(sapply(split(spl, ceiling(seq(spl) / 2)), paste, collapse = "-"))
[1] "abc-de" "fghi-j" "k-lm"   "n-o"    "p-qrst" "u-vw"   "x-yz" 

但我正在寻找更短更简洁的解决方案

有哪些可能性?

您可以将 str_extract_all 与模式 \w+(?:-\w+){0,2} 一起使用,例如查找包含 3 个单词和 2 个连字符的术语:

str <- "abc-de-fghi-j-k-lm-n-o-p-qrst-u-vw-x-yz"
n <- 2
regex <- paste0("\w+(?:-\w+){0,", n, "}")
str_extract_all(str, regex)[[1]]

[1] "abc-de-fghi" "j-k-lm"      "n-o-p"       "qrst-u-vw"   "x-yz"

n <- 3
regex <- paste0("\w+(?:-\w+){0,", n, "}")
str_extract_all(str, regex)[[1]]

[1] "abc-de-fghi-j" "k-lm-n-o"      "p-qrst-u-vw"   "x-yz"

另一种方法:首先对找到的每个 split-pattern 进行拆分,然后将 paste/collapse 分成 n-length 组,使用 split-pattern 变量作为折叠字符。

str <- "abc-de-fghi-j-k-lm-n-o-p-qrst-u-vw-x-yz"
n <- 3
pattern <- "-"

ans <- unlist(strsplit(str, pattern))
sapply(split(ans, 
             ceiling(seq_along(ans)/n)), 
       paste0, collapse = pattern)
# "abc-de-fghi"      "j-k-lm"       "n-o-p"   "qrst-u-vw"        "x-yz" 

以下情况如何(其中 'n-1' 是数字的占位符):

(?:[^-]*(?:-[^-]*){n-1})\K-

在线查看demo


  • (?: - 打开第一 non-capture 组;
    • [^-]* - 匹配其他连字符的 0+ 个字符;
    • (?: - 打开嵌套的第二个 non-capture 组;
      • -[^-]* - 匹配连字符和除连字符以外的 0+ 个字符;
      • ){n} - 关闭嵌套的 non-capture 组并匹配 n-times;
    • ) - 关闭第一个 non-capture 组;
  • \K- - 忘记我们刚刚匹配的内容并匹配结尾的连字符。

注意:使用\K表示我们必须使用PCRE(perl=TRUE)


要创建 'n-1',我们可以使用 sprintf() 功能来使用变量:

str <- "abc-de-fghi-j-k-lm-n-o-p-qrst-u-vw-x-yz"
for (n in 1:10) {
  print(strsplit(str, sprintf("(?:[^-]*(?:-[^-]*){%s})\K-", n-1), perl=TRUE)[[1]])
}

打印:

1) gsubfn gsubfn 在同名包中类似gsub 除了替换可以是函数、列表或原型对象.在原型对象的情况下,可以提供一个 fun 方法,它有一个内置的 count 变量,可用于区分事件。对于每个匹配项,匹配项都会传递给 fun 并替换为 fun.

的输出

我们使用末尾注释中显示的输入,还使用 ​​n 指定要在结果的每个元素中使用的组件数,并 sep 指定不包含的字符出现在输入中。

gsubfnsep 替换每第 n 个减号,然后 strsplit 拆分。

不需要复杂的正则表达式。

library(gsubfn)

n <- 3
sep <- " "

p <- proto(fun = function(., x) if (count %% n) "-" else sep)
strsplit(gsubfn("-", p, STR), sep)
## [[1]]
## [1] "abc-de-fghi" "j-k-lm"      "n-o-p"       "qrst-u-vw"   "x-yz"       
##
## [[2]]
## [1] "abc-de-fghi" "j-k-lm"      "n-o-p"       "qrst-u-vw"   "x-yz" 

2) rollapply 另一种方法是拆分每个 - 然后使用 rollapply 再次将其粘贴在一起,给出与 (1) 中相同的结果。

library(zoo)

roll <- function(x) rollapply(x, n, by = n, paste, collapse = "-",
  partial = TRUE, align = "left")
lapply(strsplit(STR, "-"), roll)

备注

# input
STR = "abc-de-fghi-j-k-lm-n-o-p-qrst-u-vw-x-yz"
STR <- c(STR, STR)