【问题标题】:How can I extract the unmatched portion of a string in R with regular expressions?如何使用正则表达式提取 R 中字符串的不匹配部分?
【发布时间】:2017-10-13 18:15:24
【问题描述】:

我有一个非常混乱的字符串向量。这是一个例子:

library(tidyverse)
library(stringr)
strings <- tibble(
  name = c("lorem 11:07:59 86136-1-sed", 
           "ipsum 14:35:57 S VARNAME-ut",
           "dolor 10:37:53 1513 -2-perspiciatis",
           "sit 10:48:25",
           "amet 13:52:1365293-2-unde",
           "consectetur 11:53:1 16018-2-omnis",
           "adipiscing 11:19 17237-2-iste"
           )
)
strings_out <- strings %>% 
  mutate(heads = str_extract(name, "^.*?\\s\\d{1,2}:\\d{1,2}:\\d{1,2}")) %>% 
  mutate(ends = str_replace(name, "^.*?\\s\\d{1,2}:\\d{1,2}:\\d{1,2}", ""))
strings_out[,2:3]
#> # A tibble: 7 x 2
#>                 heads                          ends
#>                 <chr>                         <chr>
#> 1      lorem 11:07:59                   86136-1-sed
#> 2      ipsum 14:35:57                  S VARNAME-ut
#> 3      dolor 10:37:53          1513 -2-perspiciatis
#> 4        sit 10:48:25                              
#> 5       amet 13:52:13                  65293-2-unde
#> 6 consectetur 11:53:1                 16018-2-omnis
#> 7                <NA> adipiscing 11:19 17237-2-iste

所以这里我有一些字符串,其中包含一些文本,然后是一个可能输入正确也可能不正确输入的时间,然后是更多文本。我想在时间之后仅提取字符串的结尾,但是它们没有任何似乎与使用str_extract 的潜在正则表达式很好对应的模式。我可以轻松匹配字符串的前半部分,如heads 所示。但是,我发现提取后半部分的唯一方法是使用带有空字符串的str_replace,如ends 所示。

我尝试在此列表中包含我注意到的所有常见错误:在时间之后没有关于连字符、间距或字符串内容的模式,在时间和所需的字符串结尾一半之间没有保证空间,缺少时间数字甚至冒号。

我想做的是能够使用str_extract 来获得与str_replace 接近的东西。关键区别在于,对于这个正则表达式仍然不起作用的错误,str_extract 给了我一个易于手动过滤和修复的NA,但str_replace 只是复制整个字符串,如第 7 行所示.

我怀疑我可以用一些更hacky的方法来做到这一点,比如获取所有NA并在Excel中手动修复或其他东西,但我很惊讶我无法弄清楚如何在一般尽管搜索和尝试不同的正则表达式,包括(^)[^]。有什么想法吗?

【问题讨论】:

  • 想要的结果是什么?
  • 很抱歉,如果不清楚。期望的结果是第二列,除了在第一列有一个的地方返回 NA 。我意识到我可以通过并在正确的索引处将它们替换为NA,但我发现“返回字符串的不匹配部分”的概念似乎很难做到。

标签: r regex string stringr


【解决方案1】:

一般来说,您可能希望查看lookarounds,但您的数据可能需要更多结构才能使它们有用。

这是我在意识到时间并不总是后面有空格之前写的一个简单示例:


library(tidyverse)
library(stringr)
strings <- tibble(
  name = c("lorem 11:07:59 86136-1-sed", 
           "ipsum 14:35:57 S VARNAME-ut",
           "dolor 10:37:53 1513 -2-perspiciatis",
           "sit 10:48:25",
           "amet 13:52:1365293-2-unde",
           "consectetur 11:53:1 16018-2-omnis",
           "adipiscing 11:19 17237-2-iste"
  )
)
strings_out <- strings %>% 
  mutate(heads = str_extract(name, "^.*?\\s\\d{1,2}:\\d{1,2}:\\d{1,2}"),
         ends = str_extract(name, "(?<=:\\d{1,2} )[\\s\\S]+$"))

strings_out[c(1,3)]
#> # A tibble: 7 x 2
#>                                  name                 ends
#>                                 <chr>                <chr>
#> 1          lorem 11:07:59 86136-1-sed          86136-1-sed
#> 2         ipsum 14:35:57 S VARNAME-ut         S VARNAME-ut
#> 3 dolor 10:37:53 1513 -2-perspiciatis 1513 -2-perspiciatis
#> 4                        sit 10:48:25                 <NA>
#> 5           amet 13:52:1365293-2-unde                 <NA>
#> 6   consectetur 11:53:1 16018-2-omnis        16018-2-omnis
#> 7       adipiscing 11:19 17237-2-iste         17237-2-iste

这里的问题是像第 5 行这样的行。如果没有更多的结构,我们无法知道时间是 13:52:13 还是 13:52:1,因为两者都是其他字符串中的选项。判断哪个是正确的不是正则表达式就能解决的问题。

【讨论】:

  • 嗯,好的,我明白你对第 5 行的意思了。据我所知,当时间后面跟着数字时,数字不超过五个,这就是我编写正则表达式的原因就像我一样。我认为lookbehind 是一个我以前不知道的工具,它确实允许使用我想要的str_extract。我想可能没有一个好的方法来“提取不匹配的,它只需要修改正则表达式来找到所需的匹配?
【解决方案2】:

你也可以试试这个:

library(tidyverse)
library(stringr)

regex = "^\\w+\\s\\d{2}:\\d{2}:*\\d{0,2}"

strings %>%
  mutate(head = str_extract(name, regex),
         end = str_replace(name, paste0(regex, "\\s?"), ""),
         end = str_replace(end, "^\\s*$", NA_character_))

结果:

# A tibble: 7 x 3
                                 name                head                  end
                                <chr>               <chr>                <chr>
1          lorem 11:07:59 86136-1-sed      lorem 11:07:59          86136-1-sed
2         ipsum 14:35:57 S VARNAME-ut      ipsum 14:35:57         S VARNAME-ut
3 dolor 10:37:53 1513 -2-perspiciatis      dolor 10:37:53 1513 -2-perspiciatis
4                        sit 10:48:25        sit 10:48:25                 <NA>
5           amet 13:52:1365293-2-unde       amet 13:52:13         65293-2-unde
6   consectetur 11:53:1 16018-2-omnis consectetur 11:53:1        16018-2-omnis
7       adipiscing 11:19 17237-2-iste    adipiscing 11:19         17237-2-iste

注意:

我的解决方案适用于第 5 行,但在这种情况下,您必须决定是要提取 13:52:13 还是 13:52:1。这两种情况都可以通过对正则表达式的简单修改来完成,但正如@Zach 所述,没有自动方法。

【讨论】:

    【解决方案3】:

    您只需多写一行即可:

    strings["rx"] <- str_match(strings$name, "\\d*:\\d*(?::\\d+)?(.*)")[,2]
    strings
    

    产量

    # A tibble: 7 x 2
                                     name                    rx
                                    <chr>                 <chr>
    1          lorem 11:07:59 86136-1-sed           86136-1-sed
    2         ipsum 14:35:57 S VARNAME-ut          S VARNAME-ut
    3 dolor 10:37:53 1513 -2-perspiciatis  1513 -2-perspiciatis
    4                        sit 10:48:25                      
    5           amet 13:52:1365293-2-unde               -2-unde
    6   consectetur 11:53:1 16018-2-omnis         16018-2-omnis
    7       adipiscing 11:19 17237-2-iste          17237-2-iste
    

    【讨论】:

    • 第 5 行呢?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-04
    相关资源
    最近更新 更多