从关闭的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] - 我不认为这些可行,因为建议使用其他模式而不是回答问题。此外,在此示例中,排名中的单词数是任意的,匹配排名的模式也适用于名字。
答案 0 :(得分:2)
我们无法从最后开始匹配,在我所知道的任何正则表达式系统中都没有任何修饰符。但是我们可以检查到最后有多少单词,并抑制我们的贪婪:)。以下正则表达式正在这样做。
^(?<rank>(?:(?:[ \t]|^)[a-z]+)+?)(?!(?:[ \t][a-z.]+){4,}$)[ \t](?<first>[a-z]+)[ \t](?:(?<middle>[a-z.]+)[ \t])?(?<last>[a-z]+)$
当你有排名第一,最后和超过1个单词时,排名部分将成为名字。
要解决这个问题,你必须定义一个排名前缀列表,这意味着有另一个词肯定会跟在它之后并以贪婪的方式捕获它。
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]+)'