@yamano357さんの{stringr}/{stringi}とbaseの文字列処理についてでちらっと私のRpubsが言及されてたので、触発されてちょっとメモ

library(stringi)

環境

Windowsです。

stri_enc_get()
## [1] "Shift_JIS"
Sys.getlocale()
## [1] "LC_COLLATE=Japanese_Japan.932;LC_CTYPE=Japanese_Japan.932;LC_MONETARY=Japanese_Japan.932;LC_NUMERIC=C;LC_TIME=Japanese_Japan.932"

まずRの挙動を確かめる

文字コードを変えると文字化けしているように見えたりするけど、中身は変わってない。

a <- b <- c <- "あ"
Encoding(b) <- "UTF-8"
Encoding(c) <- "bytes"

unlist(lapply(c(a,b,c), Encoding))
## [1] "unknown" "UTF-8"   "bytes"
# 表示のされ方は違うけど、、
cat(a)
## あ
cat(b)
## <U+0082><U+00A0>
cat(c)
## \x82\xa0
# 中身のバイト列としては変わってない
unlist(lapply(c(a,b,c), charToRaw))
## [1] 82 a0 82 a0 82 a0

UTF-8で入力してみる

a <- b <- "\xe3\x81\x82"
Encoding(b) <- "UTF-8"

unlist(lapply(c(a,b), Encoding))
## [1] "unknown" "UTF-8"
# これをやると「Error: invalid multibyte string at '<82>'」というエラーでknitできないのでコメントアウトしてるけど、文字化けする
# cat(a)

# Encodingを設定した方はちゃんと表示できる
cat(b)
## あ
# 中身のバイト列としては変わってない
unlist(lapply(c(a,b), charToRaw))
## [1] e3 81 82 e3 81 82

stringiとのやり取りをする時の変換

UTF-8の文字とShift_JISの文字を渡してみる

a <- "あ"
b <- iconv("あ", from = "Shift_JIS", to = "UTF-8")

# stri_padは指定した文字数になるように空白を詰める。つまりwidthが1だとなにもしない
a2 <- stri_pad(a, width = 1)
b2 <- stri_pad(b, width = 1)

# 返ってきた文字列は、いずれもUTF-8になる
unlist(lapply(c(a2, b2), Encoding))
## [1] "UTF-8" "UTF-8"
# 中身のバイト列も、UTF-8になっている
unlist(lapply(c(a2, b2), charToRaw))
## [1] e3 81 82 e3 81 82

つまり、Rからstringiに渡す時にUTF-8に変換されて、その変換されたものがそのまま返ってくるということ。

UTF-8への変換は、その文字オブジェクト(って言うの?)が

  1. 適切な文字コードが設定されているとき
  2. 文字コードが設定されてない(unknown)けどデフォルトの文字コード(Windowsの場合はShift_JIS)と同じとき

にうまくいくっぽい。そうでないとうまくいかないっぽい。(あんまりちゃんと調べてない)

b <- c <- "あ"
Encoding(b) <- "UTF-8"
Encoding(c) <- "bytes"

stri_pad(b, width = 1)
## Error in stri_pad_left(str, width, pad, use_length): invalid UTF-8 byte sequence detected. perhaps you should try calling stri_enc_toutf8()
stri_pad(c, width = 1)
## Error in stri_pad_left(str, width, pad, use_length): bytes encoding is not supported by this function

strinigiは元の文字コードを、stri_enc_get()で返ってくる文字コードだと仮定して動作する。なので、それを変えてやることもできる(推奨はされてない)

a <- "\xe3\x81\x82"

# stri_enc_isutf8はR側の文字コード設定に関係なく動作する。
# 逆にいうと、これがTRUEだからといって自動でUTF-8として扱われたりはしない
stri_enc_isutf8(a)
## [1] TRUE
# Shift_JIS -> UTF-8だと思って変換するので文字化けする
stri_pad(a, width = 1)
## [1] "縺\032"
# UTF-8にする
previous_enc <- stri_enc_set("UTF-8")
## You are now working with stringi_0.5.5 (ja_JP.UTF-8; ICU4C 55.1 [bundle]; Unicode 7.0)
# 今度はうまくいく...かと思いきや、なぜかEncodingが設定されなくなってて文字化けする
a2 <- stri_pad(a, width = 1)
Encoding(a2)
## [1] "unknown"
# Encodingを設定すればうまくいくけど、それはそうでしょ、という感じ。
Encoding(a2) <- "UTF-8"

# 逆にShift_JISの文字はダメ
stri_pad("あ", width = 1)
## Error in stri_pad_left(str, width, pad, use_length): invalid UTF-8 byte sequence detected. perhaps you should try calling stri_enc_toutf8()
stri_enc_set(previous_enc)
## Warning in stri_enc_set(previous_enc): This converter alias can go to
## different converter implementations. (U_AMBIGUOUS_ALIAS_WARNING)
## Warning in stri_info(short = TRUE): Your native charset is not a superset
## of US-ASCII. This may cause serious problems. Consider switching to UTF-8.
## You are now working with stringi_0.5.5 (ja_JP.Shift_JIS; ICU4C 55.1 [bundle]; Unicode 7.0)

よく分からないので触らないのが吉。

個人的な結論

とりあえず、Windowsの場合は、適切なEncodingを設定することを心掛ければ回避できる問題もあるのかも。