Excel(MS365): VBAはUnicodeに対応していない

 ExcelはUnicodeに対応していまして、例えば

"ああ😊ああ"

なんて文字列もセルに記述できるわけです。

ちなみにセル内のデータは\xl\sharedStrings.xmlにUTF-8で格納されていまして、"😊"はU+1F60A(バイナリとしてはF0 9F 98 8A)となっています。

さて、これを、例えばセルA1に記述してから

    Call MsgBox(Me.Cells(1, 1))

なんてやってみると、

化けます。

    Me.Cells(2, 1) = Me.Cells(1, 1)

とフルコピーすると正常にコピーされますが、

    Me.Cells(3, 1) = Mid(Me.Cells(1, 1), 3, 1)

とすると絵文字がうまく取り出せません。内部的には2文字扱いになっているらしく、

    Me.Cells(3, 1) = Mid(Me.Cells(1, 1), 3, 2)

で絵文字1文字が取り出せます。

"😊あ"と2文字取り出すためには

    Me.Cells(3, 1) = Mid(Me.Cells(1, 1), 3, 3)

と3文字分取り出す必要があるわけですね。

InStrとかはもっと厳しい。文字位置が化けるのみならず、そもそもVBEがUnicodeに対応していないので、コードエディタで

InStr(Me.Cells(1, 1), "😊")

と入力した時点で

InStr(Me.Cells(1, 1), ??")

になっちゃいます。

---

テキストファイルの書き出しもうまくいきません。

    Dim fn As Integer: fn = FreeFile
    Open "test.txt" For Output As #fn
    Print #fn, Me.Cells(1, 1).Value
    Close #fn

とやってみると、できあがったファイルはShift-JIS、先ほどのメッセージボックスと同じように絵文字部分が「??」となります。

まあ、VBAのファイルI/Oはネットワークパス(UNC形式)にも対応していない今時のネットワークだらけの作業環境ではあまり使い物にならないことで有名なのですが。

では、ネットワーク系にも使えるFSO(FileSystemObject)経由ならどうなのか。

VBEのメニューから ツール > 参照設定 で「Microsoft Scripting Runtime」を指定してから

    Dim fso As Scripting.FileSystemObject
    Dim ts As Scripting.TextStream
    Set fso = New Scripting.FileSystemObject
    Set ts = fso.OpenTextFile("test2.txt", ForWriting, True, TristateTrue)
    ts.Write Me.Cells(1, 1).Value
    ts.Close
    Set ts = Nothing
    Set fso = Nothing

としてやれば一応書き出せますが......「TristateTrue」だとUTF-16ですね。

今時の各種システムはUTF-8が主流なので、これではちょっときびしい。いやちょっと気の利いた市販アプリであれば複数種の文字コードに対応していたりなんなら自動判別までしてくれますが、SIが作る業務系だとそんな汎用性はコストがかさむだけなので、まあUTF-8固定なことが多いわけです。ちょっと提供時期が古かったりSierの頭が古かったりするといまだにShift-JISオンリーなんてのもありますが、今や人名地名もUnicode抜きではまともに表記できないご時世ですのでいかがなものかと。

ちなみに「TristateFalse」だとShift-JISになり、Unicode文字が混じった文字列を保存しようとするとエラーになります。

じゃあ、ADODBを使えばどうなのか。(正直VBAのファイルI/OなんでADODBを使うセオリーができているのかよくわかっていません。なんかしらの経緯があると思うんですが)

VBEのメニューから ツール > 参照設定 で「Microsoft ActiveX Data Objects 6.1 Library」を指定してから

Dim ado As ADODB.Stream
Set ado = New ADODB.Stream
ado.Charset = "UTF-8"
ado.Open
ado.WriteText Me.Cells(1, 1).Value
ado.SaveToFile "test3.txt", adSaveCreateOverWrite
ado.Close

としてやれば、おおこれはうまくいきました。
これからVBAでファイルI/O系を扱うならADODB経由がまあ無難なのではないかと。

ーーー

Microsoftが「もうVBAには抜本的な修正を加えない」と言い切っちゃってるので、ワークシートとVBAの文字コードの扱いの乖離はこれからもずっとこのままなんじゃないかなーと思っています。

最近妙に「Python使ってExcelを自動化」なんてのが流行っているようですが、あるいはこのへんの解決策への一案という面もあるのかもしれませんね。(確認していませんが)

確認環境: Excel for Microsoft365 MSO バージョン2011 ビルド13426.20308  16.0.13426.20308

0 件のコメント :

コメントを投稿