27

常識と健全性チェックを使用しgregexpr()て、以下の後読みアサーションと先読みアサーションがそれぞれ の 1 つの場所で正確に一致する必要があることを示します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()ただし、これらの一致位置は異なる方法で使用されます。後読みアサーションを使用する場合は1 つのtestString場所で分割されますが、先読みアサーションを使用する場合は2 つの場所 (2 番目の場所は間違っているようです) で分割されます。

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

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

2 つの質問があります: (Q1)ここで何が起こっているのですか? そして(Q2)どうすれstrsplit()ば行儀よくなることができますか?


更新: Theodore Lytras の優れた回答で何が起こっているかが説明されているため、(Q1)に対処します。私の答えは、(Q2)に対処する救済策を特定するために彼に基づいています。

4

3 に答える 3

29

これがバグとして認められるかどうかはわかりません。これは、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 は単一の文字を分割することでこれを解決するようstrsplitですsplit=""。この後、残りの文字列はunky townであり、一致がないため最後の分割として返されます。

各一致は分割され、残りの文字列から削除されるため、後読みは問題ありません。したがって、アルゴリズムが停止することはありません。

確かに、この動作は一見奇妙に見えます。ただし、それ以外の動作は、先読みの長さがゼロであるという仮定に違反します。アルゴリズムが文書化されていることを考えると、strsplitこれはバグの定義を満たしていないと思います。

于 2013-03-22T20:05:06.240 に答える
17

Theodore Lytras によるsubstr()の動作の慎重な説明に基づいて、合理的にクリーンな回避策は、一致する先読みアサーションの前に、任意の 1 文字に一致する肯定的な後読みアサーションを付けることです。

testString <- "take me to funky town"
FF2 <- "(?<=.)(?=funky)"
strsplit(testString, FF2, perl=TRUE)
# [[1]]
# [1] "take me to " "funky town" 
于 2013-03-22T22:17:02.147 に答える
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"

おそらく、そのようなものが回避策として機能する可能性があります。

于 2013-03-22T17:36:49.963 に答える