代码之家  ›  专栏  ›  技术社区  ›  Tyler Rinker DaniM

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

  •  15
  • Tyler Rinker DaniM  · 技术社区  · 8 年前

    我有一些文本,人们使用大写字母,中间有空格,使子字符串突出。我想替换这些子字符串之间的空格。该模式的规则是:“至少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 回复  |  直到 8 年前
        1
  •  9
  •   ctwheels    5 年前

    概述

    在R中有一种方法可以完全使用正则表达式来实现这一点,但它并不漂亮(尽管我认为它看起来很可爱!)这个答案也可以根据您的需要进行定制(最少两个大写字母,最少三个,等等)-即可扩展-并且可以匹配多个水平空白字符(不使用lookbehind,这需要固定的宽度)。


    密码

    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})

    编辑2(python)

    下面是与上面类似的python(它需要 PyPi regex 运行)。我替换了 \h 具有 [ \t] 因为PyPi正则表达式目前不支持 \h 代币

    See the working code here

    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}))
    

    使用回调

    请看 Wiktor's original answer 关于回调,这只是将他的R程序移植到python中的一个版本。这不使用PyPi正则表达式库,因此它不匹配。此外,这与Unicode不匹配。

    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))
    
        2
  •  8
  •   G. Grothendieck    8 年前

    正如我在评论中指出的那样,问题中的第一个gsubfn调用中的问题是因为正则表达式中有两个捕获组,但函数只有一个参数。这些需要匹配——两个捕获组意味着需要两个参数。通过运行此命令并查看打印语句的输出,我们可以看到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)
    

    (正如@Wiktor Stribiew在他的回答中指出的那样 backref=0 也可以。)

    3)

    gsubfn('(([A-Z]\\s+){2,}[A-Z])', x + y ~ gsub("\\s+", "", x), x)
    

    注意,它将公式解释为以下函数:

    function(x, y) gsub("\\s+", "", x)
    
        3
  •  5
  •   Wiktor Stribiżew    8 年前

    这里的问题是哪些项传递给 spacrm 函数依据 gsubfn 以及参数数量的不匹配 spacrm 函数accept和传递给它们的参数数。

    请参阅 gsubfn docs 关于 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."
    
        4
  •  1
  •   SamWhan    8 年前

    您可以简单地匹配前面有大写字母的空格,以及后面有两个由空格分隔的大写字母(使用环视)。 或者反过来 -匹配前面有两个大写字母的空格,两个大写字母之间用空格隔开,然后后跟一个大写字母。

    (?<=[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 .