为什么strsplit使用正向前瞻和后观断言匹配不同?

时间:2013-03-22 16:23:26

标签: regex r strsplit

使用gregexpr()的常识和健全性检查表明,下面的后视和前瞻断言应该只匹配testString中的一个位置:

testString <- "text XX text"
BB  <- "(?<= XX )"
FF  <- "(?= XX )"

as.vector(gregexpr(BB, testString, perl=TRUE)[[1]])
# [1] 9
as.vector(gregexpr(FF, testString, perl=TRUE)[[1]][1])
# [1] 5
但是,

strsplit()使用不同的匹配位置,在使用lookbehind断言时将testString拆分为一个位置,但是在两个位置 - 当使用前瞻断言时,第二个似乎不正确。

strsplit(testString, BB, perl=TRUE)
# [[1]]
# [1] "text XX " "text"    

strsplit(testString, FF, perl=TRUE)
# [[1]]
# [1] "text"    " "       "XX text"

我有两个问题:(Q1)这里发生了什么?并且(Q2)如何让strsplit()更好地表现?


更新:Theodore Lytras的优秀答案解释了正在发生的事情,所以地址(Q1)。我的答案建立在他的基础上,以确定一个补救措施,解决(Q2)

3 个答案:

答案 0 :(得分:26)

我不确定这是否属于错误,因为我认为这是基于R文档的预期行为。来自?strsplit

  

应用于每个输入字符串的算法是

repeat {
    if the string is empty
        break.
    if there is a match
        add the string to the left of the match to the output.
        remove the match and all to the left of it.
    else
        add the string to the output.
        break.
}
     

请注意,这意味着如果在开头有匹配项   一个(非空)字符串,输出的第一个元素是'“”',但是   如果字符串末尾有匹配,则输出为   与删除的比赛相同。

问题是前瞻(和后瞻)断言是零长度。例如,在这种情况下:

FF <- "(?=funky)"
testString <- "take me to funky town"

gregexpr(FF,testString,perl=TRUE)
# [[1]]
# [1] 12
# attr(,"match.length")
# [1] 0
# attr(,"useBytes")
# [1] TRUE

strsplit(testString,FF,perl=TRUE)
# [[1]]
# [1] "take me to " "f"           "unky town" 

发生的事情是孤独的前瞻(?=funky)在第12位匹配。所以第一次拆分包括直到位置11(比赛的左边)的字符串,并且它与字符串一起被删除,无论如何 - 长度为零。

现在剩下的字符串是funky town,前瞻符合位置1.但是没有什么可以删除,因为匹配的左边没有任何内容,而且匹配本身的长度为零。因此算法陷入无限循环。显然,R通过拆分单个字符来解决这个问题,这恰好是{{1}使用空正则表达式时的记录行为(当参数strsplit时)。在此之后,剩余的字符串为split="",由于没有匹配项,因此作为最后一次拆分返回。

Lookbehinds没有问题,因为每个匹配都被拆分并从剩余的字符串中删除,因此算法永远不会被卡住。

不可否认,这种行为乍一看似乎很奇怪。然而,否则会违反前瞻的零长度假设。鉴于记录了unky town算法,我相信这不符合错误的定义。

答案 1 :(得分:15)

基于Theodore Lytras对substr()行为的仔细解释,一个相当干净的解决方法是在匹配任何单个字符的前后断言中加上前置匹配的前瞻断言:

testString <- "take me to funky town"
FF2 <- "(?<=.)(?=funky)"
strsplit(testString, FF2, perl=TRUE)
# [[1]]
# [1] "take me to " "funky town" 

答案 2 :(得分:5)

对我来说看起来像个错误。这似乎并不仅仅与空间有关,而是与任何孤独的前瞻(正面或负面)相关:

FF <- "(?=funky)"
testString <- "take me to funky town"
strsplit(testString,FF,perl=TRUE)
# [[1]]
# [1] "take me to " "f"           "unky town"  

FF <- "(?=funky)"
testString <- "funky take me to funky funky town"
strsplit(testString,FF,perl=TRUE)
# [[1]]
# [1] "f"                "unky take me to " "f"                "unky "           
# [5] "f"                "unky town"       


FF <- "(?!y)"
testString <- "xxxyxxxxxxx"
strsplit(testString,FF,perl=TRUE)
# [[1]]
# [1] "xxx"       "y"       "xxxxxxx"

如果给出了一些与零宽度断言一起捕获的东西,似乎工作正常,例如:

FF <- " (?=XX )"
testString <- "text XX text"
strsplit(testString,FF,perl=TRUE)
# [[1]]
# [1] "text"    "XX text"

FF <- "(?= XX ) "
testString <- "text XX text"
strsplit(testString,FF,perl=TRUE)
# [[1]]
# [1] "text"    "XX text"

也许这样的事情可能会起到解决方法的作用。