从字符串末尾开始匹配

时间:2016-11-13 15:30:05

标签: r regex

从关闭的this question开始,op询问如何从字符串中提取排名,第一,中间和最后

x <- c("Marshall Robert Forsyth", "Deputy Sheriff John A. Gooch",
       "Constable Darius Quimby", "High Sheriff John Caldwell Cook")

#                                  rank             first    middle      last     
# Marshall Robert Forsyth          "Marshall"       "Robert" ""          "Forsyth"
# Deputy Sheriff John A. Gooch     "Deputy Sheriff" "John"   "A."        "Gooch"  
# Constable Darius Quimby          "Constable"      "Darius" ""          "Quimby" 
# High Sheriff John Caldwell. Cook "High Sheriff"   "John"   "Caldwell"  "Cook"

我想出了这个,只有在中间名包含句号的情况下才有效;否则,排名模式会从行首开始捕获。

pat <- '(?i)(?<rank>[a-z ]+)\\s(?<first>[a-z]+)\\s(?:(?<middle>[a-z.]+)\\s)?(?<last>[a-z]+)'

f <- function(x, pattern) {
  m <- gregexpr(pattern, x, perl = TRUE)[[1]]
  s <- attr(m, "capture.start")
  l <- attr(m, "capture.length")
  n <- attr(m, "capture.names")
  setNames(mapply('substr', x, s, s + l - 1L), n)
}

do.call('rbind', Map(f, x, pat))

#                                 rank                first      middle last     
# Marshall Robert Forsyth         "Marshall"          "Robert"   ""     "Forsyth"
# Deputy Sheriff John A. Gooch    "Deputy Sheriff"    "John"     "A."   "Gooch"  
# Constable Darius Quimby         "Constable"         "Darius"   ""     "Quimby" 
# High Sheriff John Caldwell Cook "High Sheriff John" "Caldwell" ""     "Cook"

如果中间名未给出或包含句号

,这将起作用
x <- c("Marshall Robert Forsyth", "Deputy Sheriff John A. Gooch",
       "Constable Darius Quimby", "High Sheriff John Caldwell. Cook")
do.call('rbind', Map(f, x, pat))

所以我的问题是有没有办法优先匹配字符串的匹配,以便此模式匹配last,middle,first,然后将其他所有内容保留为rank。

我是否可以在不反转字符串的情况下执行此操作或类似的hacky?此外,也许有一个更好的模式,因为我对正则表达式不是很好。

相关 - [1] [2] - 我不认为这些可行,因为建议使用其他模式而不是回答问题。此外,在此示例中,排名中的单词数是任意的,匹配排名的模式也适用于名字。

2 个答案:

答案 0 :(得分:2)

我们无法从最后开始匹配,在我所知道的任何正则表达式系统中都没有任何修饰符。但是我们可以检查到最后有多少单词,并抑制我们的贪婪:)。以下正则表达式正在这样做。

这个会做你想做的事:

^(?<rank>(?:(?:[ \t]|^)[a-z]+)+?)(?!(?:[ \t][a-z.]+){4,}$)[ \t](?<first>[a-z]+)[ \t](?:(?<middle>[a-z.]+)[ \t])?(?<last>[a-z]+)$

Live preview in regex101.com

enter image description here

还有一个例外:

当你有排名第一,最后和超过1个单词时,排名部分将成为名字。

enter image description here

要解决这个问题,你必须定义一个排名前缀列表,这意味着有另一个词肯定会跟在它之后并以贪婪的方式捕获它。

E.g。:副,高。

答案 1 :(得分:0)

我的R生锈了,但在量词之后放置?使得它在我所知道的所有正则表达式引擎中都不贪婪而不是贪婪。所以回答你的主要问题:

  

有没有办法从字符串的末尾开始匹配优先级,以便此模式匹配last,middle,first,然后将其他所有内容保留为rank?

您应该可以通过在?之后添加+来使模式的排名匹配部分非贪婪,从而实现此目的。

(?<rank>[a-z ]+?)

完整模式:

pat <- '(?i)(?<rank>[a-z ]+?)\\s(?<first>[a-z]+)\\s(?:(?<middle>[a-z.]+)\\s)?(?<last>[a-z]+)'