如何使用正则表达式在R中提取字符串中不匹配的部分?

时间:2017-10-13 18:15:24

标签: r regex string stringr

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

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或其他东西中手动修复,但我很惊讶我无法弄清楚如何返回一个不匹配的部分尽管有一堆搜索并尝试使用包含(^)[^]的不同正则表达式,但一般来说都是字符串。有什么想法吗?

3 个答案:

答案 0 :(得分: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,因为两者都是其他字符串中的选项。弄清楚哪个是正确的不是可以用正则表达式解决的问题。

答案 1 :(得分:1)

你也可以试试这个:

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:1313:52:1。这两种情况都可以通过对正则表达式的简单修改来完成,但正如@Zach所述,没有自动方式。

答案 2 :(得分:0)

你可以只用一行:

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