【问题标题】:remove non-digits except E+ and E- in string删除字符串中除 E+ 和 E- 之外的非数字
【发布时间】:2018-12-22 16:08:11
【问题描述】:

我有向量vec,我想将其转换为数字类型。因此我需要首先摆脱非数字(包括'+')。问题:当我排除它们时,'E+' 和 'E-' 中的 '+' 和 '-' 符号也会被删除。

如何删除除“E-”、“E+”和“.”之外的所有非数字来自vec?

vec = c('1234', '+ 42', '1E+4', 'NR 12', '4.5E+04', '8.6E-02')

我的方法:

gsub('[^0-9E.]', '', vec) # removes '-' and '+' in 'E-' and 'E+'

gsub('[^0-9(E\\+).]', '', vec) # includes the '+' from '+ 42' 

我想要的输出是:

c('1234', '42', '1E+4', '12', '4.5E+04', '8.6E-02')

【问题讨论】:

    标签: r regex


    【解决方案1】:

    您可以提取使用following regex的数字:

    [-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?
    

    详情

    • [-+]? - +-
    • [0-9]* - 0+ 位
    • \.? - 一个可选的.
    • [0-9]+ - 1 位以上
    • ([eE][-+]?[0-9]+)? - 一个可选的捕获组(在 ( 之后添加 ?: 以使用非捕获组)匹配 1 次或 0 次
      • [eE] - eE
      • [-+]? - 可选的 -+
      • [0-9]+ - 1 位或多位数字

    R demo:

    vec <- c('1234', '+ 42', '1E+4', 'NR 12', '4.5E+04', '8.6E-02')
    res <- regmatches(vec, regexpr("[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?", vec))
    unlist(res)
    ## => [1] "1234"    "42"      "1E+4"    "12"      "4.5E+04" "8.6E-02"
    

    如果预期字符向量中的每个项目有多个匹配项,请将 regexpr 替换为 gregexpr

    【讨论】:

      【解决方案2】:

      您可以更改您的正则表达式以检查 +- 前面是否没有 Ee 并且在这种情况下不要删除它们(使用向后查找并启用 perl=TRUE),否则, 在您的主要字符集中包含 +-,因此在任何其他情况下都将它们删除为空字符串。尝试从这里更改您的线路,

      gsub('[^0-9E.]', '', vec)
      

      到,

      gsub('(?<![Ee])[+-]|[^0-9E.+-]', '', vec, perl=TRUE)
      

      【讨论】:

        【解决方案3】:

        在遇到数字之前,您不能匹配数字:

        ^\D+(?=(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?)
        

        那会匹配:

        • ^ 字符串开头
        • \D+ 匹配 1+ 次不是数字
        • (?= 积极前瞻
          • (?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)? 用 E 或 e 匹配数字或十进制数
        • ) 关闭正向预测

        例如:

        vec = c('1234', '+ 42', '1E+4', 'NR 12', '4.5E+04', '8.6E-02')
        print(gsub('^\\D+(?=(?:[0-9]+(?:\\.[0-9]*)?|\\.[0-9]+)(?:[eE][+-]?[0-9]+)?)', '', vec, perl=TRUE))
        # [1] "1234"    "42"      "1E+4"    "12"      "4.5E+04" "8.6E-02"
        

        Regex demo | R demo

        【讨论】:

        • 如果 OP 有像 NR 12 NR 这样的数据,这将不起作用,这就是为什么 OP 想要替换字符串中不是来自此字符集 [^0-9E.+-] 的每个字符,除非 +- 紧随 E
        • @PushpeshKumarRajwanshi 正则表达式以锚点开头,因此这是正确的,尽管该数据不在示例数据中。
        • 是的,您的正则表达式对于 OP 给出的数据集是正确的。我的观点是,OP 已经在使用比您的正则表达式更强大的策略,因为它会处理我给 NR 12 NR 的示例,该示例不适用于您的正则表达式,因为它假定非数字部分只会出现在之前数字部分。
        【解决方案4】:

        这行得通:

        str_remove_all(vec,"\\D.* ")
        

        【讨论】:

          【解决方案5】:

          或者,在 R 基础中并使用gsub

          gsub("\\D.* ", "", vec)
          

          【讨论】:

            【解决方案6】:

            您提到您想要一个数字结果,但您将您想要的输出声明为字符输出(我忽略了这一点,只是使用数字)。

            library(tidyverse)
            library(stringr)
            
            vec <- c('1234', '+ 42', '1E+4', 'NR 12', '4.5E+04', '8.6E-02')
            
            vec %>%
              str_extract_all("(\\+|\\-)*[:digit:]+(\\.)?[:digit:]*", simplify = TRUE) %>%
              apply(2, as.numeric) %>%
              as_tibble() %>%
              mutate(V2 = ifelse(is.na(V2), 0, V2)) %>%
              mutate(result = V1*10^V2)
            

            结果:

            # A tibble: 6 x 3
                  V1    V2    result
               <dbl> <dbl>     <dbl>
            1 1234       0  1234    
            2   42       0    42    
            3    1       4 10000    
            4   12       0    12    
            5    4.5     4 45000    
            6    8.6    -2     0.086
            

            ...然后是.$result,当然。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-06-24
              相关资源
              最近更新 更多