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

R 替换多个(3+;)大写字母之间的空格,r,regex,gsubfn,R,Regex,Gsubfn,我有一些文字,人们用大写字母和空格来突出子字符串。我想替换这些子字符串之间的空格。该模式的规则是:“至少3个连续的大写字母,每个字母之间留有空格” 我很好奇如何使用纯正则表达式和gsubfn包来实现这一点,因为我认为这对它来说是一项简单的工作,但在下面的MWE示例中,由于在其中放置了一个额外的字母,我崩溃并烧毁了它(我很好奇为什么会发生这种情况) MWE 正如我在评论中指出的,问题中的第一个gsubfn调用中的问题是由于正则表达式中有两个捕获组,而函数只有一个参数。这些需要匹配——两个捕获组意味

我有一些文字,人们用大写字母和空格来突出子字符串。我想替换这些子字符串之间的空格。该模式的规则是:“至少3个连续的大写字母,每个字母之间留有空格”

我很好奇如何使用纯正则表达式和gsubfn包来实现这一点,因为我认为这对它来说是一项简单的工作,但在下面的MWE示例中,由于在其中放置了一个额外的字母,我崩溃并烧毁了它(我很好奇为什么会发生这种情况)

MWE
正如我在评论中指出的,问题中的第一个gsubfn调用中的问题是由于正则表达式中有两个捕获组,而函数只有一个参数。这些需要匹配——两个捕获组意味着需要两个参数。通过运行此命令并查看print语句的输出,我们可以看到gsubfn传递了什么:

junk <- gsubfn('(([A-Z]\\s+){2,}[A-Z])', ~ print(list(...)), x)
请注意,它将公式解释为函数:

function (...) gsub("\\s+", "", ..1)
function(x, y) gsub("\\s+", "", x)
我们可以这样查看由公式生成的函数:

fn$identity( ~ gsub("\\s+", "", ..1) )
## function (...) 
## gsub("\\s+", "", ..1)
2)这使用问题中的正则表达式和问题中的函数,但添加backref=-1参数,该参数告诉它只将第一个捕获组传递给函数——减号表示也不会传递整个匹配

gsubfn('(([A-Z]\\s+){2,}[A-Z])', spacrm1, x, backref = -1)
(正如@Wiktor Stribiżew在他的回答中指出的那样,
backref=0
也会起作用。)

3)使用问题中的正则表达式表达这一点的另一种方式是:

gsubfn('(([A-Z]\\s+){2,}[A-Z])', x + y ~ gsub("\\s+", "", x), x)
请注意,它将公式解释为以下函数:

function (...) gsub("\\s+", "", ..1)
function(x, y) gsub("\\s+", "", x)

这里的问题是
gsubfn
将哪些项传递给
spacrm
函数,以及参数数量
spacrm
函数接受与传递给它们的参数数量不匹配

请参阅about
backref
参数:

要传递给函数的反向引用数。如果为零或正,则将匹配项作为第一个参数传递给替换函数,后跟指定数量的反向引用作为后续参数。如果为负数,则只传递反向引用数,但不传递匹配本身。如果省略,将自动确定,即,如果没有反向引用,则为0,否则等于反向引用数。它通过计算模式中非转义左括号的数量来确定这一点

因此,在您的例子中,
backref
参数被省略,而
spacrmX
函数值被忽略

只接受一个参数的
spacrm1
函数得到两个参数,因此
未使用的参数(“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."
spacrm1概述
在R中,有一种完全使用正则表达式的方法可以做到这一点,但它并不漂亮(尽管我认为它看起来很可爱!)这个答案也可以根据您的需要定制(最小两个大写字母,最小三个字母,等等),即可伸缩性,并且可以匹配多个水平空白字符(不使用lookbehinds,后者需要固定的宽度)


代码

替换:空字符串


编辑1(非ASCII字母) 我的原始模式使用了
\b
,这可能不适用于Unicode字符(如
É
)。以下替代方案可能是更好的方法。它会检查以确保第一个大写字符前面的不是字母(来自任何语言/脚本)。它还确保如果后跟任何其他字母,则它与大写字母序列末尾的大写字符不匹配

如果还需要确保数字不在大写字母之前,可以使用
[^\p{L}\p{N}]
代替
\p{L}

输出
解释
  • (?:(?=(?:\b\p{Lu}\h+{2}\p{Lu})|\G(?!\A))
    匹配以下任一项
    • (?=\b(?:\p{Lu}\h+{2}\p{Lu})
      正向前瞻确保后面的内容匹配(在本例中用作断言,以查找字符串中格式为
      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(python) 下面是与上面类似的python(需要运行)。我将
\h
替换为
[\t]
,因为PyPi regex当前不支持
\h
令牌

以上正则表达式基于第一个正则表达式。如果您希望使用第二个正则表达式,请使用以下命令:

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

您可以简单地匹配前面有大写字母的空格,以及后面有两个由空格分隔的大写字母(
(?:(?=\b(?:\p{Lu}\h+){2}\p{Lu})|\G(?!\A))\p{Lu}\K\h+(?=\p{Lu})
(?:(?<=\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.
import regex
a = [
    "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."
]

r = regex.compile(r"(?:(?=\b(?:\p{Lu} +){2}\p{Lu})|\G(?!\A))\p{Lu}\K +(?=\p{Lu})")
for i in a:
    print(r.sub('',i))
(?:(?<=\P{L})(?=(?:\p{Lu}[ \t]+){2}\p{Lu})|\G(?!\A))\p{Lu}\K[ \t]+(?=\p{Lu}(?!\p{L}))
import re
a = [
    "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."
]

def repl(m):
    return re.sub(r"\s+",'',m.group(0))

for i in a:
    print(re.sub(r"(?:[A-Z]\s+){2,}[A-Z]", repl, i))
(?<=[A-Z]) (?=[A-Z] [A-Z])|(?<=[A-Z] [A-Z]) (?=[A-Z])
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)