您可以将sub 与以下 PCRE 正则表达式一起使用:
.*(?|(dog).*(cat)|(cat).*(dog)).*
请参阅regex demo。
详情
-
.* - 除换行符之外的任何 0+ 字符(要匹配所有字符,请在模式开始处添加 (?s))
-
(?|(dog).*(cat)|(cat).*(dog)) - 一个分支重置组 (?|...|...) 匹配两个备选方案之一:
-
(dog).*(cat) - 第 1 组捕获 dog,然后是尽可能多的 0+ 个字符,第 2 组捕获 cat
-
| - 或
-
(cat).*(dog) - 第 1 组捕获 cat,然后尽可能多的任何 0+ 字符,第 2 组捕获 dog(在分支重置组中,组 ID 重置为组 + 1 之前的值)
-
.* - 除换行符以外的任何 0+ 个字符
\1 \2 替换模式将第 1 组和第 2 组值插入到结果字符串中(因此结果只是dog 或cat、一个空格和一个cat 或dog)。
也见an R demo online:
x <- c("aasdfadsf cat asdfadsf dog", "asfdadsfads dog asdfasdfadsf cat")
sub(".*(?|(dog).*(cat)|(cat).*(dog)).*", "\\1 \\2", x, perl=TRUE)
## => [1] "cat dog" "dog cat"
若要在不匹配的情况下返回NA,请使用正则表达式匹配特定模式或整个字符串,并将其与gsubfn 一起使用以应用自定义替换逻辑:
> gsubfn("^(?:.*((dog).*(giraffe)|(giraffe).*(dog)).*|.*)$", function(x,a,b,y,z,i) ifelse(nchar(x)>0, paste0(a,y," ",b,z), NA), x)
[1] "NA" "NA"
> gsubfn("^(?:.*((dog).*(cat)|(cat).*(dog)).*|.*)$", function(x,a,b,y,z,i) ifelse(nchar(x)>0, paste0(a,y," ",b,z), NA), x)
[1] "cat dog" "dog cat"
这里,
-
^ - 字符串的开头 anchor
-
(?:.*((dog).*(cat)|(cat).*(dog)).*|.*) - non-capturing group 匹配以下两种选择中的任何一种:
.*((dog).*(cat)|(cat).*(dog)).*:
-
.* - 尽可能多的任何 0+ 个字符
-
((dog).*(cat)|(cat).*(dog)) - 一个 capturing group 匹配两个选项之一:
-
(dog).*(cat) - dog(第 2 组,分配给 a 变量),任意 0+ 个字符尽可能多,然后 cat(第 3 组,分配给 b 变量)
|
-
(cat).*(dog) - dog(第4组,分配给y变量),任意0+个字符尽可能多,然后cat(第5组,分配给z变量)
-
.* - 任何 0+ 字符尽可能多
-
$ - 字符串结尾 anchor。
匿名函数中的x代表Group 1的值,这里是“技术”,我们用nchar检查Group 1匹配长度是否不为零,如果不为空,我们替换为自定义逻辑,如果第1组为空,我们用NA替换。