この間関数を作っているときに、文字列をstringr
でいじっているときに、正規表現で文字列をマッチングしたときに自分が想定していたのと異なる挙動をしたのでメモしておく。
問題となる処理
{stringr}
でstr_detct
したときに問題の処理があった。文字列に数字だけの文字列があるのかマッチングしたいときに、(格好つけて)名前付き文字クラスが予め定義されている正規表現のパターンをつかってマッチングしたかった。
どそこで、数値の定義されている名前付き文字クラスの[:digit:]
でマッチングしようと思った。
x <- c("昭和", "64", "年") stringr::str_detect(x, pattern = "[:digit:]")
## [1] FALSE TRUE FALSE
となって、数字のところだけTRUE
で返ってきて嬉しい。
いろいろ処理しているときに、
x <- c("30", "30", 30) stringr::str_detect(x, pattern = "[:digit:]")
## [1] TRUE TRUE TRUE
となることに気づいた。いや、全角の「30」って数値なの!?と。全角って文字じゃないのか!?と。
この処理は個人的には全角と半角の判別を兼ねて使いたかったので意外な挙動だった。
調べてみた
これは、{stringr}
だけでなく、Rで文字列処理していると起こる問題?だということがわかった。
たとえば、{base}
の文字列マッチでstr_detct
と同じように理論型で結果を返すgrepl
も
x <- c("30", "30", 30) grepl(pattern = "[[:digit:]]", x)
## [1] TRUE TRUE TRUE
と、同じく全角の30も数値として結果を返す。
調べてみたら、 [http://www.okadajp.org/RWiki/?R+%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE#content_1_2:title] によると名前付き文字クラスで予め定義されているものはロケール依存だということがわかった。
つまり、日本語環境下では30も数字だし、30も数字なので間違った挙動はしていなかったということである。
しかし、R上での数値と言ったら半角数字だけで、全角数字は文字として扱われると思っていただけに意外だった。
回避策
全角半角を区別して半角数字のみを拾いたかったら
x <- c("30", "30", 30) stringr::str_detect(x, pattern = "[0123456789]")
## [1] TRUE FALSE TRUE
と格好つけずに素直に描くと、全角はマッチングしなくなる。
grepl
のような{base}
系なら
x <- c("30", "30", 30) grepl(pattern = "[0123456789]", x)
## [1] TRUE FALSE TRUE
でもいいし、格好つけて[:digit:]
を使いたかったら
x <- c("30", "30", 30) grepl(pattern = "[[:digit:]]", x, perl = TRUE)
## [1] TRUE FALSE TRUE
とパール風正規表現を使うことでもいける。
残念ながらパール風正規表現を使うオプションがstringr
系にはないので、なにか方法があるのかもしれないが、今のところは諦めるしかない。