Shift-JISのByte型配列をデコードして文字列を返す関数

不採用バージョン

競馬データJVDataのレコードは文字コードShift-JISのByte型配列なので、これを引数に指定した位置と長さの配列のデータからデコードした文字列を返す関数DecordShiftJISを作りました。

しばらくこの関数を使っていたのですが、後に示す採用バージョンの方が速いしコードも少ないので今は使ってません…

' 文字コードShift-JISのByte型配列のPositionからPosition + Length - 1個の要素をデコードしてUnicode文字列を返す
Function DecordShiftJIS(ByRef EncodedChars() As Byte, ByRef Position As Long, ByRef Length As Long) As String

  Dim DecodedChars As TextSystem
  Dim HexSpace     As String * 6
  Dim i As Long

  ' 引数のPositionとLengthが配列のインデックス外なら終了する
  If Position < LBound(EncodedChars) Or UBound(EncodedChars) < Position + Length - 1 Then
    DecordShiftJIS = ""
    Exit Function
  End If

  Set DecodedChars = New TextSystem
  Mid(HexSpace, 1, 2) = "&H"  ' マルチバイト用の16進数文字スペースを用意する
  i = Position ' Record配列用インデックス
  Do
    If (129 <= EncodedChars(i) And EncodedChars(i) <= 159) Or _
       (224 <= EncodedChars(i) And EncodedChars(i) <= 252) Then
      ' マルチバイトの場合は2バイト目を合わせて文字変換する
      ' 10進数数値 --> 16進数文字列 --> 2バイト分16進数文字列結合 --> 2バイト分10進数数値 --> 1文字 と変換する
      Mid(HexSpace, 3, 2) = Hex(EncodedChars(i))
      Mid(HexSpace, 5, 2) = Hex(EncodedChars(i + 1))
      DecodedChars.Add Chr(val(HexSpace))
      i = i + 2
    Else
      ' シングルバイトの場合は1バイト目のみで文字変換する
      DecodedChars.Add Chr(EncodedChars(i))
      i = i + 1
    End If
  Loop While i <= Position + Length - 1

  DecordShiftJIS = DecodedChars.Text

  Set DecodedChars = Nothing

End Function

この関数DecordShiftJISは引数1が1 ByteずつShift-JISのバイナリデータが入っている配列で、引数2がデコードしたい配列の要素の始めのインデックス、引数3が要素の数です。

Shift-JISは1 Byteで1文字を表現する文字(アルファベット、数字など)と、2 Bytesで1文字を表現する文字(ひらがな、漢字など)があります。この場合分けは1 Byte目の数値からできます。

シングルバイト(1 Byteで1文字)の場合は、Chr(バイナリデータ)とすればデコードできます。マルチバイト(2 Bytesで1文字)の場合は、2 Byteのバイナリデータを合わせて16進数の文字列にして、Chr(Val(16進数の文字列))でデコードしています。

マルチバイトのデコードの方法は周りくどいのであんまり良くないと思うのですが、他に方法が思いつかないのでこうしています。VB.NetだとEncodingオブジェクトがあるそうですが、VBAでも使えるか調べていません。

デコードした文字列の結合に大量の文字列結合を高速処理できるMidステートメントを使ったクラス - Qiita:kosukemaedaを使っています。

前のポストでも説明したようにVBAは文字列の結合の処理がとても遅いです。TextSystemクラスは文字列結合処理を速くしようとMidステートメントの置換を利用しています。それでも1000回、10000回と文字列結合を繰り返すと時間がかかります。結合した文字列の値渡しにもいくらか時間がかかります。

採用バージョン

Private Function DecordShiftJIS(ByRef EncodedChars() As Byte, ByRef Position As Long, ByRef Length As Long) As String

  Dim Extracted() As Byte

  Extracted = MidB(EncodedChars, Position, Length)
  DecordShiftJIS = StrConv(Extracted, vbUnicode)
  Erase Extracted

End Function

Shift-JISのByte型配列のある部分をString型に変換するのに、MidB()StrConv()でできることがわかりました。上の回りくどい方法より全然良いのでこっちを使っています。

Kosuke Maeda / まえだこうすけ

「機械学習で競馬予想して勝てるのか?」をテーマに活動中! QiitaにはR、VBAなどのTipsを投稿しています。