从字符串末尾开始匹配

raw*_*awr 9 regex r

这个被关闭的问题开始,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"
Run Code Online (Sandbox Code Playgroud)

我想出了这个,只有当中间名包含一个句号时才有效; 否则,排名模式会从行首开始捕获.

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"
Run Code Online (Sandbox Code Playgroud)

因此,如果中间名未给出或包含一段时间,这将起作用

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))
Run Code Online (Sandbox Code Playgroud)

所以我的问题是有没有办法从字符串结尾优先匹配,这样这个模式匹配last,middle,first,然后把所有其他东西留给rank.

我可以这样做而不会扭转字符串或像这样的hacky?此外,也许有一个更好的模式,因为我不是很好的正则表达式.


相关 - [1] [2] - 我认为这些不会起作用,因为建议采用另一种模式而不是回答这个问题.此外,在该示例中,等级中的单词的数量是任意的,并且匹配等级的模式也适用于第一名称.

Nik*_*tOn 2

我们无法从末尾开始匹配,在我所知道的任何正则表达式系统中都没有任何修饰符。但我们可以检查一下到最后为止我们还有多少字,并克制我们的贪婪:)。下面的正则表达式正在执行此操作。

这将做你想做的事:

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

regex101.com 中的实时预览

在此输入图像描述

还有一个例外:

当您的排名有名字、姓氏和超过 1 个单词时,排名部分将成为名字。

在此输入图像描述

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

例如:副,高级。