替换多个(3+)大写字母之间的空格

时间:2017-11-22 13:42:49

标签: r regex gsubfn

我有一些文字,其中人们使用大写字母,其间有空格,以使子字符串脱颖而出。我想替换这些子串之间的空格。该模式的规则是:“至少3个连续的大写字母,每个字母之间有一个空格”。

我很好奇如何使用纯正则表达式以及 gsubfn 包这样做,因为我认为这对它来说是一件容易的工作但是在下面的MWE示例中我崩溃并作为一个额外的烧毁信被放在那里(我很好奇为什么会这样)。

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."

4 个答案:

答案 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)。

代码

See regex in use here

(?:(?=\b(?:\p{Lu}\h+){2}\p{Lu})|\G(?!\A))\p{Lu}\K\h+(?=\p{Lu})

替换:空字符串

修改

我刚刚意识到我在这里使用\b,这可能不适用于Unicode字符(例如É)。以下替代方案可能是更好的方法。它检查以确保在第一个大写字符不是字母(来自任何语言/脚本)之前的内容。它还确保它与大写系列末尾的大写字符不匹配,如果后跟任何其他字母。

如果您还需要确保数字不在大写字母之前,则可以在[^\p{L}\p{N}]的位置使用\P{L}

See regex in use here

(?:(?<=\P{L})(?=(?:\p{Lu}\h+){2}\p{Lu})|\G(?!\A))\p{Lu}\K\h+(?=\p{Lu}(?!\p{L}))

用法

See code in use here

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)

Live here at ideone