我有一些文字,其中人们使用大写字母,其间有空格,以使子字符串脱颖而出。我想替换这些子串之间的空格。该模式的规则是:“至少3个连续的大写字母,每个字母之间有一个空格”。
我很好奇如何使用纯正则表达式以及 gsubfn 包这样做,因为我认为这对它来说是一件容易的工作但是在下面的MWE示例中我崩溃并作为一个额外的烧毁信被放在那里(我很好奇为什么会这样)。
x <- c(
'Welcome to A I: the best W O R L D!',
'Hi I R is the B O M B for sure: we A G R E E indeed.'
)
## first to show I have the right regex pattern
gsub('(([A-Z]\\s+){2,}[A-Z])', '<FOO>', x)
## [1] "Welcome to A I: the best <FOO>!"
## [2] "Hi I R is the <FOO> for sure: we <FOO> indeed."
library(gsubfn)
spacrm1 <- function(string) {gsub('\\s+', '', string)}
gsubfn('(([A-Z]\\s+){2,}[A-Z])', spacrm1, x)
## Error in (function (string) : unused argument ("L ")
## "Would love to understand why this error is happening"
spacrm2 <- function(...) {gsub('\\s+', '', paste(..., collapse = ''))}
gsubfn('(([A-Z]\\s+){2,}[A-Z])', spacrm2, x)
## [1] "Welcome to A I: the best WORLDL!"
## [2] "Hi I R is the BOMBM for sure: we AGREEE indeed."
## "Would love to understand why the extra letter is happening"
[1] "Welcome to A I: the best WORLD!"
[2] "Hi I R is the BOMB for sure: we AGREE indeed."
答案 0 :(得分:8)
正如我在评论中指出的那样,问题中第一个gsubfn调用中的问题来自于正则表达式中有两个捕获组,但函数只有一个参数。这些需要匹配 - 两个捕获组意味着需要两个参数。我们可以通过运行它并查看print语句的输出来查看gsubfn传递的内容:
junk <- gsubfn('(([A-Z]\\s+){2,}[A-Z])', ~ print(list(...)), x)
我们可以通过以下任何方式解决此问题:
1)这使用了问题中的正则表达式,但使用了一个接受多个参数的函数。实际上只在函数中使用了第一个参数。
gsubfn('(([A-Z]\\s+){2,}[A-Z])', ~ gsub("\\s+", "", ..1), x)
## [1] "Welcome to A I: the best WORLD!"
## [2] "Hi I R is the BOMB for sure: we AGREE indeed."
请注意,它将公式解释为函数:
function (...) gsub("\\s+", "", ..1)
我们可以像这样查看公式生成的函数:
fn$identity( ~ gsub("\\s+", "", ..1) )
## function (...)
## gsub("\\s+", "", ..1)
2)这使用了问题中的正则表达式以及问题中的函数,但添加了backref = -1参数,该参数告诉它只将第一个捕获组传递给函数 - 减号意味着不要通过整场比赛。
gsubfn('(([A-Z]\\s+){2,}[A-Z])', spacrm1, x, backref = -1)
(正如@WiktorStribiżew在答案backref=0
中指出的那样也会奏效。)
3)使用问题中的正则表达式来表达这一点的另一种方法是:
gsubfn('(([A-Z]\\s+){2,}[A-Z])', x + y ~ gsub("\\s+", "", x), x)
请注意,它将公式解释为此函数:
function(x, y) gsub("\\s+", "", x)
答案 1 :(得分:8)
R中有一种方法可以完全使用正则表达式,但它并不漂亮(虽然我觉得它看起来很漂亮!)这个答案也可以根据你的需要进行定制(两个大写最小,三个最小等等)。 ) - 即可伸缩 - 并且可以匹配多个水平空白字符(不使用需要固定宽度的lookbehinds)。
(?:(?=\b(?:\p{Lu}\h+){2}\p{Lu})|\G(?!\A))\p{Lu}\K\h+(?=\p{Lu})
替换:空字符串
我刚刚意识到我在这里使用\b
,这可能不适用于Unicode字符(例如É
)。以下替代方案可能是更好的方法。它检查以确保在第一个大写字符不是字母(来自任何语言/脚本)之前的内容。它还确保它与大写系列末尾的大写字符不匹配,如果后跟任何其他字母。
如果您还需要确保数字不在大写字母之前,则可以在[^\p{L}\p{N}]
的位置使用\P{L}
。
(?:(?<=\P{L})(?=(?:\p{Lu}\h+){2}\p{Lu})|\G(?!\A))\p{Lu}\K\h+(?=\p{Lu}(?!\p{L}))
x <- c(
"Welcome to A I: the best W O R L D!",
"Hi I R is the B O M B for sure: we A G R E E indeed."
)
gsub("(?:(?=\\b(?:\\p{Lu}\\h+){2}\\p{Lu})|\\G(?!\\A))\\p{Lu}\\K\\h+(?=\\p{Lu})", "", x, perl=TRUE)
Welcome to A I: the best W O R L D!
Hi I R is the B O M B for sure: we A G R E E indeed.
Welcome to A I: the best WORLD!
Hi I R is the BOMB for sure: we AGREE indeed.
(?:(?=(?:\b\p{Lu}\h+){2}\p{Lu})|\G(?!\A))
匹配以下任一项
(?=\b(?:\p{Lu}\h+){2}\p{Lu})
确定后续匹配的正向前瞻(在这种情况下用作断言以查找字符串中格式为A A A
的所有位置)。您还可以在此正面预测的末尾添加\b
,以确保I A Name
之类的内容无法匹配
\b
在字边界处断言位置(?:\p{Lu}\h+){2}
完全匹配以下两次
\p{Lu}
匹配任何语言的大写字符(Unicode)\h+
匹配一个或多个水平空白字符\p{Lu}
匹配任何语言的大写字符(Unicode)\G(?!\A)
在上一场比赛结束时断言位置\p{Lu}
匹配任何语言的大写字符(Unicode)\K
重置报告的匹配的起点。最终匹配中不再包含任何以前消费的字符\h+
匹配一个或多个水平空白字符(?=\p{Lu})
确保以下内容的正面预测是任何语言的大写字符(Unicode)答案 2 :(得分:5)
这里的问题是spacrm
函数传递给gsubfn
函数的函数以及参数数量spacrm
函数接受的不匹配以及传递给它们的参数数量。< / p>
请参阅gsubfn
docs关于backref
参数:
要传递给函数的反向引用数。如果为零或正,则匹配作为替换函数的第一个参数传递,后跟指示的反向引用数作为后续参数。 如果为负,则仅传递该反向引用数但匹配本身不是。 如果省略,将自动确定,即如果没有反向引用则为0和否则它将等于反向引用的数量。它通过计算模式中未转义的左括号的数量来确定这一点。
因此,在您的情况下,backref
参数被省略,spacrmX
函数got W O R L D
and L
值。
只接受单个参数的spacrm1
函数有两个参数,因此unused argument ("L ")
错误。
当使用spacrm2
时,它获得了所有两个捕获的值,并且它们被连接在一起(删除空格后)。
您实际上可能只是使用backref=0
告诉gsubfn
仅处理整个匹配值并简化模式,删除捕获组并使用一个非捕获组:
spacrm1 <- function(string) {gsub('\\s+', '', string)}
x <- c(
'Welcome to A I: the best W O R L D!',
'Hi I R is the B O M B for sure: we A G R E E indeed.'
)
gsubfn('(?:[A-Z]\\s+){2,}[A-Z]', spacrm2, x, backref=0)
[1] "Welcome to A I: the best WORLD!"
[2] "Hi I R is the BOMB for sure: we AGREE indeed."
答案 3 :(得分:1)
您可以简单地匹配以大写字母开头的空格,以及后跟两个由空格分隔的大写字母(使用环视)。 或者相反 - 匹配前面有两个用空格分隔的大写字母的空格,然后是一个大写字母。
(?<=[A-Z]) (?=[A-Z] [A-Z])|(?<=[A-Z] [A-Z]) (?=[A-Z])
R-代码:
x <- c(
"Welcome to A I: the best W O R L D!",
"Hi I R is the B O M B for sure: we A G R E E indeed."
)
gsub("(?<=[A-Z]) (?=[A-Z] [A-Z])|(?<=[A-Z] [A-Z]) (?=[A-Z])", "", x, perl=TRUE)