【问题标题】:equivalent regular expression to remove all punctuations删除所有标点符号的等效正则表达式
【发布时间】:2013-12-19 04:50:21
【问题描述】:

在 R 中,要从字符串中删除标点符号,我可以这样做:

x <- 'a#,g:?s!*$t/{u}\d\&y'
gsub('[[:punct:]]','',x)
[1] "agstudy"

这很聪明,但我没有严格控制删除的标点符号(想象一下我想在我的角色中保留一些符号)。如何以更明确的方式重写此 gsub 而不会忘记任何符号,如下所示:

gsub('[#,:?!*$/{}\\&]','',x,perl=FALSE)

编辑

我遇到的困难是如何编写正则表达式(我更喜欢在R中)从x中删除所有标点符号,只保留#例如:

 "a#gstudy"

【问题讨论】:

    标签: regex r perl


    【解决方案1】:

    使用否定的前瞻断言:

    x <- 'a#,g:?s!*$t/{u}\\d\\&y'
    
    gsub('(?!#)[[:punct:]]','',x, perl=TRUE)
    # [1] "a#gstudy"
    

    这实质上是对每个字符进行两次测试,一次从前面的字符间空间询问下一个字符是否不是"#",然后从字符本身询问它是否是标点符号。如果两个测试都为真,则注册匹配并删除该字符。

    【讨论】:

    • 谢谢。你认为我会遇到一些性能问题(我有一个庞大的数据集要过滤)吗?
    • 我不知道,但不难比较(?!#)[[:punct:]][[:punct:]] 的速度,以了解负前瞻在多大程度上减慢了速度。
    【解决方案2】:

    您可以使用否定字符类,例如:

    \pP 是标点符号的 unicode 字符类。

    \PP 不是标点符号。

    [^\PP] 就是一个标点符号。

    [^\PP~] 是除了波浪号之外的所有标点符号。

    注意:您可以使用\p{PosixPunct}保持在ASCII范围内:

    [^\P{PosixPunct}~]

    或者在\p{XPosixPunct}的ASCII范围内使用具有这种特殊性的unicode标点字符:

    [^\P{XPosixPunct}~]

    【讨论】:

    • 有趣+1!我如何在 R/perl 中使用它?例如 gsub('[^p#]','',x,perl=F) ,保留 # 但它会删除所有其他好的字符...
    • @agstudy: 不,\pP\PP 是字符类,如 \w\W,但用于标点符号。对于你的例子,你必须写[^\PP#]
    • 谢谢。我几乎接近 gsub('[^\\PP#]','',x,perl=TRUE) ,我得到 ` "agstudy#$"` ,我不知道为什么最后有美元($)。有什么想法吗?
    • @agstudy:原因是$ 不是 unicode 的标点符号。但是,如果您使用 posix 版本 \P{PosixPunct} 则不同,因为 ASCII 范围内的标点符号定义如下:“所有不是控件、空格或字母数字字符。
    【解决方案3】:

    阅读this page 表示[[:punct:]] 字符应包括:

    [-!"#$%&'()*+,./:;<=>?@[\\\]^_`{|}~]
    

    从 R ?regex 页面,我们也得到了这个作为验证:

    [:punct:]
    Punctuation characters:
    ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
    

    因此,您可以将其用作创建自己的模式的基础,不包括要保留的字符。


    这真是一团糟,尤其是有两个更好的答案,但我只是想表明我的想法:

    创建一个看起来像这样的函数:

    newPunks <- function(CHARS) {
      punks <- c("!", "\\\"", "#", "\\$", "%", "&", "'", "\\(", "\\)",
                 "\\*", "\\+", ",", "-", "\\.", "/", ":", ";", "<",
                 "=", ">", "\\?", "@", "\\[", "\\\\", "\\]", "\\^", "_", 
                 "`", "\\{", "\\|", "\\}", "~")
      keepers <- strsplit(CHARS, "")[[1]]
      keepers <- ifelse(keepers %in% c("\"", "$", "{", "}", "(", ")",
                                       "*", "+", ".", "?", "[", "]",
                                       "^", "|", "\\"), paste0("\\", keepers), keepers)
      paste(setdiff(punks, keepers), collapse="|")
    }
    

    用法:

    gsub(newPunks("#"), "", x)
    # [1] "a#gstudy"
    gsub(newPunks(""), "", x)
    # [1] "agstudy"
    gsub(newPunks("&#{"), "", x)
    # [1] "a#gst{ud&y"
    

    哎呀。我该睡觉了……

    【讨论】:

    • 感谢 +1 的链接。在 gsub 中(手动)编写正则表达式的困难。但我会做到的!
    • @agstudy,我要编辑的重点是,既然我们知道这个类包含哪些字符,我们可能可以创建自己的模式,也许使用这些字符的向量和setdiff 或别的东西。
    • 是和不是。我们应该知道符号,以及如何在 gsub 中编写正则表达式,例如 gsub([-!"#$%&amp;'()*+,./:;&lt;=&gt;?@[/\/\\]^_{|}~],'',x)` 将失败。伙计,(对我来说)在这里写正则表达式并不容易。
    【解决方案4】:

    它在 Perl 中的工作方式完全相同,[:punct:] 是一个简单映射到的 POSIX 字符类:

    [!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]
    

    等效的 Perl 版本是:

    my $x = 'a#,g:?s!*$t/{u}\d\&y';
    $x =~ s/[[:punct:]]//g;
    print $x;
    
    __END__
    agstudy
    

    【讨论】:

    • 谢谢。问题不是要有等效的 perl 代码,而是使用符号的等效正则表达式(手动)。
    • 不确定当时为什么要标记 Perl,但字符类只是映射到上面列出的字符。
    • Perl 被标记是因为,可以在 R 中使用 perl 正则表达式。grep(pattern, x, ignore.case = FALSE, perl = TRUE,..)
    【解决方案5】:

    直接的方法是使用前瞻或后瞻来匹配同一个字符两次,一次是为了确保它是一个标点符号,一次是为了确保它不是“#”。

    (?=[^#])[[:punct:]]
    

    (?!#)[[:punct:]]
    

    不过,Lookahead 和lookbehinds 有点贵。与其在每个位置都使用环视,不如在找到标点符号时只使用一个更有效。

    [[:punct:]](?<!#)
    

    当然,完全摆脱环视会更有效。这可以通过双重否定来实现。

    [^[:^punct:]#]
    

    我还没有使用 R 测试过这些,但它们至少应该可以使用 perl=TRUE

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-21
      • 1970-01-01
      • 2015-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多