がらぱっぱ

自分用覚え書き中心(モバイル関係中心だったはずが)

Tips【VBA】ADODBを使ってメモリtoメモリで文字コード変換を行う(ファイルに書かない)

VBAを使ってWEBのデータを取り込んでEXCELに貼り付けようといったときに、文字コードの変換の必要があります。
UTF-8とか、Shift-JISとかで取り出した(帰ってきた)データをVBAの文字列(UNICODE)に変換する方法の覚え書き

ADODB.Streamをつかう

マニュアルはここらへんかな?
Stream オブジェクト (ADO) | Microsoft Docs
WebでADODB.Streamで文字コード変換を検索しても、ファイルに書き込むとかファイルから読み込むとかばかりでなかなか見つからない。
肝心のマニュアルもサンプルとか全然ないのでいまいち。
今回やりたいのはメモリtoメモリなのでファイル名とかURLとか指定せずにStreamで完結させる。

ストリームでオープンしてバイナリモードで書き込んで、テキストモードで読み込めばいいじゃん

ということでこんな感じで関数

ソース
'
' 文字コード変換を行い文字列(UNICODE)にする
' 引数 bin 元のバイナリデータ(変換前)
'         encoding 元の文字コード(UTF-8、Shift-JIS etc)
'
Public Function GetString(ByRef bin, ByVal encoding As String) As String
    Const adTypeBinary = 1
    Const adTypeText = 2
    With CreateObject("ADODB.Stream")
        .Open
        .Type = adTypeBinary
        .Write bin
        .Position = 0
        .Type = adTypeText
        .Charset = encoding
        GetString = .ReadText
        .Close
    End With
End Function
説明

オープンして、バイナリで書き込んで、ポインタを先頭(0)にして、テキストモードで(エンコーディング指定して)読み込む
ってそのまんまやん

さて、性能は?

CreateObjectしたり、Open/Closeしたり(ファイルとかに書き込んでないはずだけど)オーバーヘッドが気になります。

性能測定

こんな感じで時間計測処理を組み込みます。
変数aに適当なUTF-8とかの文字列を入れて

  startTimer = Timer
  a = GetString(a, "utf_8")
  Debug.Print Len(a)
  Debug.Print "time=" & (Timer - startTimer)
 30620 
time=0.0078125
 30620 
time=0.0390625
 30620 
time=0.0078125

30Kbyteくらいの文字列の変換が数ms~数十ms

ちなみに長さを表示しているDebug.Print Len(a)をコメントアウトすると

time=0
time=0
time=0
time=0.0234375
time=0
time=0.015625
time=0
time=0
time=0.0234375
time=0

ほとんど影響ないくらいの時間ですね。
この関数をループの中から大量に呼び出すとかはあまり考えられないので、毎回CreateObjectしてもあまり問題なさそうですし。

Charsetってどんなのが設定できるの?

代表的なものは以下くらいでしょうか
utf-8shift_jiseuc-jp、iso-2022-jpunicode
※最近だとeucとかiso-2022-jp文字コードのページはほとんど見かけませんねぇ

Charsetを自動判定にすると便利

Charsetに _autodetect_allを設定すると内容を判断して自動で文字コードを判定してくれます。
_autodetectというのもあるようですが、こいつは結構文字化けしちゃうので使えない。
文字コードがわかる場合は指定した方がいいです。